Coverage Report

Created: 2025-06-13 06:38

/src/icu/icu4c/source/i18n/ucol_res.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) 1996-2016, International Business Machines
6
*   Corporation and others.  All Rights Reserved.
7
*******************************************************************************
8
*   file name:  ucol_res.cpp
9
*   encoding:   UTF-8
10
*   tab size:   8 (not used)
11
*   indentation:4
12
*
13
* Description:
14
* This file contains dependencies that the collation run-time doesn't normally
15
* need. This mainly contains resource bundle usage and collation meta information
16
*
17
* Modification history
18
* Date        Name      Comments
19
* 1996-1999   various members of ICU team maintained C API for collation framework
20
* 02/16/2001  synwee    Added internal method getPrevSpecialCE
21
* 03/01/2001  synwee    Added maxexpansion functionality.
22
* 03/16/2001  weiv      Collation framework is rewritten in C and made UCA compliant
23
* 12/08/2004  grhoten   Split part of ucol.cpp into ucol_res.cpp
24
* 2012-2014   markus    Rewritten in C++ again.
25
*/
26
27
#include "unicode/utypes.h"
28
29
#if !UCONFIG_NO_COLLATION
30
31
#include "unicode/coll.h"
32
#include "unicode/localpointer.h"
33
#include "unicode/locid.h"
34
#include "unicode/tblcoll.h"
35
#include "unicode/ucol.h"
36
#include "unicode/uloc.h"
37
#include "unicode/unistr.h"
38
#include "unicode/ures.h"
39
#include "charstr.h"
40
#include "cmemory.h"
41
#include "cstring.h"
42
#include "collationdatareader.h"
43
#include "collationroot.h"
44
#include "collationtailoring.h"
45
#include "resource.h"
46
#include "putilimp.h"
47
#include "uassert.h"
48
#include "ucln_in.h"
49
#include "ucol_imp.h"
50
#include "uenumimp.h"
51
#include "ulist.h"
52
#include "umutex.h"
53
#include "unifiedcache.h"
54
#include "uresimp.h"
55
#include "ustrenum.h"
56
#include "utracimp.h"
57
58
U_NAMESPACE_BEGIN
59
60
namespace {
61
62
const char16_t* rootRules = nullptr;
63
int32_t rootRulesLength = 0;
64
UResourceBundle* rootBundle = nullptr;
65
UInitOnce gInitOnceUcolRes{};
66
67
}  // namespace
68
69
U_CDECL_BEGIN
70
71
static UBool U_CALLCONV
72
0
ucol_res_cleanup() {
73
0
    rootRules = nullptr;
74
0
    rootRulesLength = 0;
75
0
    ures_close(rootBundle);
76
0
    rootBundle = nullptr;
77
0
    gInitOnceUcolRes.reset();
78
0
    return true;
79
0
}
80
81
void U_CALLCONV
82
0
CollationLoader::loadRootRules(UErrorCode &errorCode) {
83
0
    if(U_FAILURE(errorCode)) { return; }
84
0
    rootBundle = ures_open(U_ICUDATA_COLL, kRootLocaleName, &errorCode);
85
0
    if(U_FAILURE(errorCode)) { return; }
86
0
    rootRules = ures_getStringByKey(rootBundle, "UCARules", &rootRulesLength, &errorCode);
87
0
    if(U_FAILURE(errorCode)) {
88
0
        ures_close(rootBundle);
89
0
        rootBundle = nullptr;
90
0
        return;
91
0
    }
92
0
    ucln_i18n_registerCleanup(UCLN_I18N_UCOL_RES, ucol_res_cleanup);
93
0
}
94
95
U_CDECL_END
96
97
void
98
0
CollationLoader::appendRootRules(UnicodeString &s) {
99
0
    UErrorCode errorCode = U_ZERO_ERROR;
100
0
    umtx_initOnce(gInitOnceUcolRes, CollationLoader::loadRootRules, errorCode);
101
0
    if(U_SUCCESS(errorCode)) {
102
0
        s.append(rootRules, rootRulesLength);
103
0
    }
104
0
}
105
106
void
107
CollationLoader::loadRules(const char *localeID, const char *collationType,
108
4.87k
                           UnicodeString &rules, UErrorCode &errorCode) {
109
4.87k
    if(U_FAILURE(errorCode)) { return; }
110
4.87k
    U_ASSERT(collationType != nullptr && *collationType != 0);
111
    // Copy the type for lowercasing.
112
4.87k
    char type[16];
113
4.87k
    int32_t typeLength = static_cast<int32_t>(uprv_strlen(collationType));
114
4.87k
    if(typeLength >= UPRV_LENGTHOF(type)) {
115
21
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
116
21
        return;
117
21
    }
118
4.87k
    uprv_memcpy(type, collationType, typeLength + 1);
119
4.85k
    T_CString_toLowerCase(type);
120
121
4.85k
    LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_COLL, localeID, &errorCode));
122
4.85k
    LocalUResourceBundlePointer collations(
123
4.85k
            ures_getByKey(bundle.getAlias(), "collations", nullptr, &errorCode));
124
4.85k
    LocalUResourceBundlePointer data(
125
4.85k
            ures_getByKeyWithFallback(collations.getAlias(), type, nullptr, &errorCode));
126
4.85k
    int32_t length;
127
4.85k
    const char16_t *s =  ures_getStringByKey(data.getAlias(), "Sequence", &length, &errorCode);
128
4.85k
    if(U_FAILURE(errorCode)) { return; }
129
130
    // No string pointer aliasing so that we need not hold onto the resource bundle.
131
4.81k
    rules.setTo(s, length);
132
4.81k
    if(rules.isBogus()) {
133
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
134
0
    }
135
4.81k
}
136
137
template<> U_I18N_API
138
const CollationCacheEntry *
139
LocaleCacheKey<CollationCacheEntry>::createObject(const void *creationContext,
140
6.59k
                                                  UErrorCode &errorCode) const {
141
6.59k
    CollationLoader *loader =
142
6.59k
            reinterpret_cast<CollationLoader *>(
143
6.59k
                    const_cast<void *>(creationContext));
144
6.59k
    return loader->createCacheEntry(errorCode);
145
6.59k
}
146
147
const CollationCacheEntry *
148
14.3k
CollationLoader::loadTailoring(const Locale &locale, UErrorCode &errorCode) {
149
14.3k
    const CollationCacheEntry *rootEntry = CollationRoot::getRootCacheEntry(errorCode);
150
14.3k
    if(U_FAILURE(errorCode)) { return nullptr; }
151
14.3k
    const char *name = locale.getName();
152
14.3k
    if(*name == 0 || uprv_strcmp(name, "root") == 0) {
153
154
        // Have to add a ref.
155
546
        rootEntry->addRef();
156
546
        return rootEntry;
157
546
    }
158
159
    // Clear warning codes before loading where they get cached.
160
13.8k
    errorCode = U_ZERO_ERROR;
161
13.8k
    CollationLoader loader(rootEntry, locale, errorCode);
162
163
    // getCacheEntry adds a ref for us.
164
13.8k
    return loader.getCacheEntry(errorCode);
165
14.3k
}
166
167
CollationLoader::CollationLoader(const CollationCacheEntry *re, const Locale &requested,
168
                                 UErrorCode &errorCode)
169
13.8k
        : cache(UnifiedCache::getInstance(errorCode)), rootEntry(re),
170
13.8k
          validLocale(re->validLocale), locale(requested),
171
13.8k
          typesTried(0), typeFallback(false),
172
13.8k
          bundle(nullptr), collations(nullptr), data(nullptr) {
173
13.8k
    type[0] = 0;
174
13.8k
    defaultType[0] = 0;
175
13.8k
    if(U_FAILURE(errorCode)) { return; }
176
177
13.8k
    if (locale.isBogus()) {
178
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
179
0
        return;
180
0
    }
181
    // Canonicalize the locale ID: Ignore all irrelevant keywords.
182
13.8k
    const char *baseName = locale.getBaseName();
183
13.8k
    if(uprv_strcmp(locale.getName(), baseName) != 0) {
184
2.24k
        locale = Locale(baseName);
185
        // Due to ICU-22416, we may have bogus locale constructed from
186
        // a string of getBaseName().
187
2.24k
        if (locale.isBogus()) {
188
1
            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
189
1
            return;
190
1
        }
191
192
        // Fetch the collation type from the locale ID.
193
2.24k
        int32_t typeLength = requested.getKeywordValue("collation",
194
2.24k
                type, UPRV_LENGTHOF(type) - 1, errorCode);
195
2.24k
        if(U_FAILURE(errorCode)) {
196
387
            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
197
387
            return;
198
387
        }
199
1.85k
        type[typeLength] = 0;  // in case of U_NOT_TERMINATED_WARNING
200
1.85k
        if(typeLength == 0) {
201
            // No collation type.
202
1.39k
        } else if(uprv_stricmp(type, "default") == 0) {
203
            // Ignore "default" (case-insensitive).
204
1
            type[0] = 0;
205
465
        } else {
206
            // Copy the collation type.
207
465
            T_CString_toLowerCase(type);
208
465
            locale.setKeywordValue("collation", type, errorCode);
209
465
        }
210
1.85k
    }
211
13.8k
}
212
213
13.8k
CollationLoader::~CollationLoader() {
214
13.8k
    ures_close(data);
215
13.8k
    ures_close(collations);
216
13.8k
    ures_close(bundle);
217
13.8k
}
218
219
const CollationCacheEntry *
220
6.59k
CollationLoader::createCacheEntry(UErrorCode &errorCode) {
221
    // This is a linear lookup and fallback flow turned into a state machine.
222
    // Most local variables have been turned into instance fields.
223
    // In a cache miss, cache.get() calls CacheKey::createObject(),
224
    // which means that we progress via recursion.
225
    // loadFromCollations() will recurse to itself as well for collation type fallback.
226
6.59k
    if(bundle == nullptr) {
227
5.87k
        return loadFromLocale(errorCode);
228
5.87k
    } else if(collations == nullptr) {
229
463
        return loadFromBundle(errorCode);
230
463
    } else if(data == nullptr) {
231
249
        return loadFromCollations(errorCode);
232
249
    } else {
233
14
        return loadFromData(errorCode);
234
14
    }
235
6.59k
}
236
237
const CollationCacheEntry *
238
5.87k
CollationLoader::loadFromLocale(UErrorCode &errorCode) {
239
5.87k
    if(U_FAILURE(errorCode)) { return nullptr; }
240
5.87k
    U_ASSERT(bundle == nullptr);
241
5.87k
    bundle = ures_openNoDefault(U_ICUDATA_COLL, locale.getBaseName(), &errorCode);
242
5.87k
    if(errorCode == U_MISSING_RESOURCE_ERROR) {
243
0
        errorCode = U_USING_DEFAULT_WARNING;
244
245
        // Have to add that ref that we promise.
246
0
        rootEntry->addRef();
247
0
        return rootEntry;
248
0
    }
249
5.87k
    Locale requestedLocale(locale);
250
5.87k
    const char *vLocale = ures_getLocaleByType(bundle, ULOC_ACTUAL_LOCALE, &errorCode);
251
5.87k
    if(U_FAILURE(errorCode)) { return nullptr; }
252
5.86k
    locale = validLocale = Locale(vLocale);  // no type until loadFromCollations()
253
5.86k
    if(type[0] != 0) {
254
446
        locale.setKeywordValue("collation", type, errorCode);
255
446
    }
256
5.86k
    if(locale != requestedLocale) {
257
5.33k
        return getCacheEntry(errorCode);
258
5.33k
    } else {
259
537
        return loadFromBundle(errorCode);
260
537
    }
261
5.86k
}
262
263
const CollationCacheEntry *
264
1.00k
CollationLoader::loadFromBundle(UErrorCode &errorCode) {
265
1.00k
    if(U_FAILURE(errorCode)) { return nullptr; }
266
1.00k
    U_ASSERT(collations == nullptr);
267
    // There are zero or more tailorings in the collations table.
268
1.00k
    collations = ures_getByKey(bundle, "collations", nullptr, &errorCode);
269
1.00k
    if(errorCode == U_MISSING_RESOURCE_ERROR) {
270
0
        errorCode = U_USING_DEFAULT_WARNING;
271
        // Return the root tailoring with the validLocale, without collation type.
272
0
        return makeCacheEntryFromRoot(validLocale, errorCode);
273
0
    }
274
1.00k
    if(U_FAILURE(errorCode)) { return nullptr; }
275
276
    // Fetch the default type from the data.
277
1.00k
    {
278
1.00k
        UErrorCode internalErrorCode = U_ZERO_ERROR;
279
1.00k
        LocalUResourceBundlePointer def(
280
1.00k
                ures_getByKeyWithFallback(collations, "default", nullptr, &internalErrorCode));
281
1.00k
        int32_t length;
282
1.00k
        const char16_t *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
283
1.00k
        if(U_SUCCESS(internalErrorCode) && 0 < length && length < UPRV_LENGTHOF(defaultType)) {
284
1.00k
            u_UCharsToChars(s, defaultType, length + 1);
285
1.00k
        } else {
286
0
            uprv_strcpy(defaultType, "standard");
287
0
        }
288
1.00k
    }
289
290
    // Record which collation types we have looked for already,
291
    // so that we do not deadlock in the cache.
292
    //
293
    // If there is no explicit type, then we look in the cache
294
    // for the entry with the default type.
295
    // If the explicit type is the default type, then we do not look in the cache
296
    // for the entry with an empty type.
297
    // Otherwise, two concurrent requests with opposite fallbacks would deadlock each other.
298
    // Also, it is easier to always enter the next method with a non-empty type.
299
1.00k
    if(type[0] == 0) {
300
652
        uprv_strcpy(type, defaultType);
301
652
        typesTried |= TRIED_DEFAULT;
302
652
        if(uprv_strcmp(type, "search") == 0) {
303
0
            typesTried |= TRIED_SEARCH;
304
0
        }
305
652
        if(uprv_strcmp(type, "standard") == 0) {
306
612
            typesTried |= TRIED_STANDARD;
307
612
        }
308
652
        locale.setKeywordValue("collation", type, errorCode);
309
652
        return getCacheEntry(errorCode);
310
652
    } else {
311
348
        if(uprv_strcmp(type, defaultType) == 0) {
312
0
            typesTried |= TRIED_DEFAULT;
313
0
        }
314
348
        if(uprv_strcmp(type, "search") == 0) {
315
4
            typesTried |= TRIED_SEARCH;
316
4
        }
317
348
        if(uprv_strcmp(type, "standard") == 0) {
318
1
            typesTried |= TRIED_STANDARD;
319
1
        }
320
348
        return loadFromCollations(errorCode);
321
348
    }
322
1.00k
}
323
324
const CollationCacheEntry *
325
597
CollationLoader::loadFromCollations(UErrorCode &errorCode) {
326
597
    if(U_FAILURE(errorCode)) { return nullptr; }
327
597
    U_ASSERT(data == nullptr);
328
    // Load the collations/type tailoring, with type fallback.
329
597
    LocalUResourceBundlePointer localData(
330
597
            ures_getByKeyWithFallback(collations, type, nullptr, &errorCode));
331
597
    int32_t typeLength = static_cast<int32_t>(uprv_strlen(type));
332
597
    if(errorCode == U_MISSING_RESOURCE_ERROR) {
333
322
        errorCode = U_USING_DEFAULT_WARNING;
334
322
        typeFallback = true;
335
322
        if((typesTried & TRIED_SEARCH) == 0 &&
336
322
                typeLength > 6 && uprv_strncmp(type, "search", 6) == 0) {
337
            // fall back from something like "searchjl" to "search"
338
4
            typesTried |= TRIED_SEARCH;
339
4
            type[6] = 0;
340
318
        } else if((typesTried & TRIED_DEFAULT) == 0) {
341
            // fall back to the default type
342
318
            typesTried |= TRIED_DEFAULT;
343
318
            uprv_strcpy(type, defaultType);
344
318
        } else if((typesTried & TRIED_STANDARD) == 0) {
345
            // fall back to the "standard" type
346
0
            typesTried |= TRIED_STANDARD;
347
0
            uprv_strcpy(type, "standard");
348
0
        } else {
349
            // Return the root tailoring with the validLocale, without collation type.
350
0
            return makeCacheEntryFromRoot(validLocale, errorCode);
351
0
        }
352
322
        locale.setKeywordValue("collation", type, errorCode);
353
322
        return getCacheEntry(errorCode);
354
322
    }
355
275
    if(U_FAILURE(errorCode)) { return nullptr; }
356
357
275
    data = localData.orphan();
358
275
    const char *actualLocale = ures_getLocaleByType(data, ULOC_ACTUAL_LOCALE, &errorCode);
359
275
    if(U_FAILURE(errorCode)) { return nullptr; }
360
275
    const char *vLocale = validLocale.getBaseName();
361
275
    UBool actualAndValidLocalesAreDifferent = Locale(actualLocale) != Locale(vLocale);
362
363
    // Set the collation types on the informational locales,
364
    // except when they match the default types (for brevity and backwards compatibility).
365
    // For the valid locale, suppress the default type.
366
275
    if(uprv_strcmp(type, defaultType) != 0) {
367
30
        validLocale.setKeywordValue("collation", type, errorCode);
368
30
        if(U_FAILURE(errorCode)) { return nullptr; }
369
30
    }
370
371
    // Is this the same as the root collator? If so, then use that instead.
372
275
    if((*actualLocale == 0 || uprv_strcmp(actualLocale, "root") == 0) &&
373
275
            uprv_strcmp(type, "standard") == 0) {
374
36
        if(typeFallback) {
375
0
            errorCode = U_USING_DEFAULT_WARNING;
376
0
        }
377
36
        return makeCacheEntryFromRoot(validLocale, errorCode);
378
36
    }
379
380
239
    locale = Locale(actualLocale);
381
239
    if(actualAndValidLocalesAreDifferent) {
382
70
        locale.setKeywordValue("collation", type, errorCode);
383
70
        const CollationCacheEntry *entry = getCacheEntry(errorCode);
384
70
        return makeCacheEntry(validLocale, entry, errorCode);
385
169
    } else {
386
169
        return loadFromData(errorCode);
387
169
    }
388
239
}
389
390
const CollationCacheEntry *
391
183
CollationLoader::loadFromData(UErrorCode &errorCode) {
392
183
    if(U_FAILURE(errorCode)) { return nullptr; }
393
183
    LocalPointer<CollationTailoring> t(new CollationTailoring(rootEntry->tailoring->settings));
394
183
    if(t.isNull() || t->isBogus()) {
395
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
396
0
        return nullptr;
397
0
    }
398
399
    // deserialize
400
183
    LocalUResourceBundlePointer binary(ures_getByKey(data, "%%CollationBin", nullptr, &errorCode));
401
    // Note: U_MISSING_RESOURCE_ERROR --> The old code built from rules if available
402
    // but that created undesirable dependencies.
403
183
    int32_t length = 0;
404
183
    const uint8_t *inBytes = ures_getBinary(binary.getAlias(), &length, &errorCode);
405
183
    CollationDataReader::read(rootEntry->tailoring, inBytes, length, *t, errorCode);
406
    // Note: U_COLLATOR_VERSION_MISMATCH --> The old code built from rules if available
407
    // but that created undesirable dependencies.
408
183
    if(U_FAILURE(errorCode)) { return nullptr; }
409
410
    // Try to fetch the optional rules string.
411
180
    {
412
180
        UErrorCode internalErrorCode = U_ZERO_ERROR;
413
180
        int32_t len;
414
180
        const char16_t *s = ures_getStringByKey(data, "Sequence", &len,
415
180
                                             &internalErrorCode);
416
180
        if(U_SUCCESS(internalErrorCode)) {
417
180
            t->rules.setTo(true, s, len);
418
180
        }
419
180
    }
420
421
180
    const char *actualLocale = locale.getBaseName();  // without type
422
180
    const char *vLocale = validLocale.getBaseName();
423
180
    UBool actualAndValidLocalesAreDifferent = Locale(actualLocale) != Locale(vLocale);
424
425
    // For the actual locale, suppress the default type *according to the actual locale*.
426
    // For example, zh has default=pinyin and contains all of the Chinese tailorings.
427
    // zh_Hant has default=stroke but has no other data.
428
    // For the valid locale "zh_Hant" we need to suppress stroke.
429
    // For the actual locale "zh" we need to suppress pinyin instead.
430
180
    if(actualAndValidLocalesAreDifferent) {
431
        // Opening a bundle for the actual locale should always succeed.
432
13
        LocalUResourceBundlePointer actualBundle(
433
13
                ures_open(U_ICUDATA_COLL, actualLocale, &errorCode));
434
13
        if(U_FAILURE(errorCode)) { return nullptr; }
435
13
        UErrorCode internalErrorCode = U_ZERO_ERROR;
436
13
        LocalUResourceBundlePointer def(
437
13
                ures_getByKeyWithFallback(actualBundle.getAlias(), "collations/default", nullptr,
438
13
                                          &internalErrorCode));
439
13
        int32_t len;
440
13
        const char16_t *s = ures_getString(def.getAlias(), &len, &internalErrorCode);
441
13
        if(U_SUCCESS(internalErrorCode) && len < UPRV_LENGTHOF(defaultType)) {
442
13
            u_UCharsToChars(s, defaultType, len + 1);
443
13
        } else {
444
0
            uprv_strcpy(defaultType, "standard");
445
0
        }
446
13
    }
447
180
    t->actualLocale = locale;
448
180
    if(uprv_strcmp(type, defaultType) != 0) {
449
19
        t->actualLocale.setKeywordValue("collation", type, errorCode);
450
161
    } else if(uprv_strcmp(locale.getName(), locale.getBaseName()) != 0) {
451
        // Remove the collation keyword if it was set.
452
4
        t->actualLocale.setKeywordValue("collation", nullptr, errorCode);
453
4
    }
454
180
    if(U_FAILURE(errorCode)) { return nullptr; }
455
456
180
    if(typeFallback) {
457
4
        errorCode = U_USING_DEFAULT_WARNING;
458
4
    }
459
180
    t->bundle = bundle;
460
180
    bundle = nullptr;
461
180
    const CollationCacheEntry *entry = new CollationCacheEntry(validLocale, t.getAlias());
462
180
    if(entry == nullptr) {
463
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
464
0
        return nullptr;
465
180
    } else {
466
180
        t.orphan();
467
180
    }
468
    // Have to add that reference that we promise.
469
180
    entry->addRef();
470
180
    return entry;
471
180
}
472
473
const CollationCacheEntry *
474
20.2k
CollationLoader::getCacheEntry(UErrorCode &errorCode) {
475
20.2k
    LocaleCacheKey<CollationCacheEntry> key(locale);
476
20.2k
    const CollationCacheEntry *entry = nullptr;
477
20.2k
    cache->get(key, this, entry, errorCode);
478
20.2k
    return entry;
479
20.2k
}
480
481
const CollationCacheEntry *
482
CollationLoader::makeCacheEntryFromRoot(
483
        const Locale &/*loc*/,
484
36
        UErrorCode &errorCode) const {
485
36
    if (U_FAILURE(errorCode)) {
486
0
        return nullptr;
487
0
    }
488
36
    rootEntry->addRef();
489
36
    return makeCacheEntry(validLocale, rootEntry, errorCode);
490
36
}
491
492
const CollationCacheEntry *
493
CollationLoader::makeCacheEntry(
494
        const Locale &loc,
495
        const CollationCacheEntry *entryFromCache,
496
106
        UErrorCode &errorCode) {
497
106
    if(U_FAILURE(errorCode) || loc == entryFromCache->validLocale) {
498
40
        return entryFromCache;
499
40
    }
500
66
    CollationCacheEntry *entry = new CollationCacheEntry(loc, entryFromCache->tailoring);
501
66
    if(entry == nullptr) {
502
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
503
0
        entryFromCache->removeRef();
504
0
        return nullptr;
505
0
    }
506
66
    entry->addRef();
507
66
    entryFromCache->removeRef();
508
66
    return entry;
509
66
}
510
511
U_NAMESPACE_END
512
513
U_NAMESPACE_USE
514
515
U_CAPI UCollator*
516
ucol_open(const char *loc,
517
          UErrorCode *status)
518
0
{
519
0
    UTRACE_ENTRY_OC(UTRACE_UCOL_OPEN);
520
0
    UTRACE_DATA1(UTRACE_INFO, "locale = \"%s\"", loc);
521
0
    UCollator *result = nullptr;
522
523
0
    Collator *coll = Collator::createInstance(loc, *status);
524
0
    if(U_SUCCESS(*status)) {
525
0
        result = coll->toUCollator();
526
0
    }
527
0
    UTRACE_EXIT_PTR_STATUS(result, *status);
528
0
    return result;
529
0
}
530
531
532
U_CAPI int32_t U_EXPORT2
533
ucol_getDisplayName(    const    char        *objLoc,
534
                    const    char        *dispLoc,
535
                    char16_t          *result,
536
                    int32_t         resultLength,
537
                    UErrorCode        *status)
538
0
{
539
0
    if(U_FAILURE(*status)) return -1;
540
0
    UnicodeString dst;
541
0
    if(!(result==nullptr && resultLength==0)) {
542
        // nullptr destination for pure preflighting: empty dummy string
543
        // otherwise, alias the destination buffer
544
0
        dst.setTo(result, 0, resultLength);
545
0
    }
546
0
    Collator::getDisplayName(Locale(objLoc), Locale(dispLoc), dst);
547
0
    return dst.extract(result, resultLength, *status);
548
0
}
549
550
U_CAPI const char* U_EXPORT2
551
ucol_getAvailable(int32_t index)
552
0
{
553
0
    int32_t count = 0;
554
0
    const Locale *loc = Collator::getAvailableLocales(count);
555
0
    if (loc != nullptr && index < count) {
556
0
        return loc[index].getName();
557
0
    }
558
0
    return nullptr;
559
0
}
560
561
U_CAPI int32_t U_EXPORT2
562
ucol_countAvailable()
563
0
{
564
0
    int32_t count = 0;
565
0
    Collator::getAvailableLocales(count);
566
0
    return count;
567
0
}
568
569
#if !UCONFIG_NO_SERVICE
570
U_CAPI UEnumeration* U_EXPORT2
571
0
ucol_openAvailableLocales(UErrorCode *status) {
572
    // This is a wrapper over Collator::getAvailableLocales()
573
0
    if (U_FAILURE(*status)) {
574
0
        return nullptr;
575
0
    }
576
0
    StringEnumeration *s = icu::Collator::getAvailableLocales();
577
0
    if (s == nullptr) {
578
0
        *status = U_MEMORY_ALLOCATION_ERROR;
579
0
        return nullptr;
580
0
    }
581
0
    return uenum_openFromStringEnumeration(s, status);
582
0
}
583
#endif
584
585
// Note: KEYWORDS[0] != RESOURCE_NAME - alan
586
587
static const char RESOURCE_NAME[] = "collations";
588
589
static const char* const KEYWORDS[] = { "collation" };
590
591
0
#define KEYWORD_COUNT UPRV_LENGTHOF(KEYWORDS)
592
593
U_CAPI UEnumeration* U_EXPORT2
594
0
ucol_getKeywords(UErrorCode *status) {
595
0
    UEnumeration *result = nullptr;
596
0
    if (U_SUCCESS(*status)) {
597
0
        return uenum_openCharStringsEnumeration(KEYWORDS, KEYWORD_COUNT, status);
598
0
    }
599
0
    return result;
600
0
}
601
602
U_CAPI UEnumeration* U_EXPORT2
603
0
ucol_getKeywordValues(const char *keyword, UErrorCode *status) {
604
0
    if (U_FAILURE(*status)) {
605
0
        return nullptr;
606
0
    }
607
    // hard-coded to accept exactly one collation keyword
608
    // modify if additional collation keyword is added later
609
0
    if (keyword==nullptr || uprv_strcmp(keyword, KEYWORDS[0])!=0)
610
0
    {
611
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
612
0
        return nullptr;
613
0
    }
614
0
    return ures_getKeywordValues(U_ICUDATA_COLL, RESOURCE_NAME, status);
615
0
}
616
617
static const UEnumeration defaultKeywordValues = {
618
    nullptr,
619
    nullptr,
620
    ulist_close_keyword_values_iterator,
621
    ulist_count_keyword_values,
622
    uenum_unextDefault,
623
    ulist_next_keyword_value,
624
    ulist_reset_keyword_values_iterator
625
};
626
627
namespace {
628
629
struct KeywordsSink : public ResourceSink {
630
public:
631
    KeywordsSink(UErrorCode &errorCode) :
632
0
            values(ulist_createEmptyList(&errorCode)), hasDefault(false) {}
633
    virtual ~KeywordsSink();
634
635
    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
636
0
                     UErrorCode &errorCode) override {
637
0
        if (U_FAILURE(errorCode)) { return; }
638
0
        ResourceTable collations = value.getTable(errorCode);
639
0
        for (int32_t i = 0; collations.getKeyAndValue(i, key, value); ++i) {
640
0
            UResType type = value.getType();
641
0
            if (type == URES_STRING) {
642
0
                if (!hasDefault && uprv_strcmp(key, "default") == 0) {
643
0
                    CharString defcoll;
644
0
                    defcoll.appendInvariantChars(value.getUnicodeString(errorCode), errorCode);
645
0
                    if (U_SUCCESS(errorCode) && !defcoll.isEmpty()) {
646
0
                        char *ownedDefault = uprv_strdup(defcoll.data());
647
0
                        if (ownedDefault == nullptr) {
648
0
                            errorCode = U_MEMORY_ALLOCATION_ERROR;
649
0
                            return;
650
0
                        }
651
0
                        ulist_removeString(values, defcoll.data());
652
0
                        ulist_addItemBeginList(values, ownedDefault, true, &errorCode);
653
0
                        hasDefault = true;
654
0
                    }
655
0
                }
656
0
            } else if (type == URES_TABLE && uprv_strncmp(key, "private-", 8) != 0) {
657
0
                if (!ulist_containsString(values, key, static_cast<int32_t>(uprv_strlen(key)))) {
658
0
                    ulist_addItemEndList(values, key, false, &errorCode);
659
0
                }
660
0
            }
661
0
            if (U_FAILURE(errorCode)) { return; }
662
0
        }
663
0
    }
664
665
    UList *values;
666
    UBool hasDefault;
667
};
668
669
0
KeywordsSink::~KeywordsSink() {
670
0
    ulist_deleteList(values);
671
0
}
672
673
}  // namespace
674
675
U_CAPI UEnumeration* U_EXPORT2
676
ucol_getKeywordValuesForLocale(const char* /*key*/, const char* locale,
677
0
                               UBool /*commonlyUsed*/, UErrorCode* status) {
678
    // Note: The parameter commonlyUsed is not used.
679
    // The switch is in the method signature for consistency
680
    // with other locale services.
681
682
    // Read available collation values from collation bundles.
683
0
    LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_COLL, locale, status));
684
0
    KeywordsSink sink(*status);
685
0
    ures_getAllItemsWithFallback(bundle.getAlias(), RESOURCE_NAME, sink, *status);
686
0
    if (U_FAILURE(*status)) { return nullptr; }
687
688
0
    UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
689
0
    if (en == nullptr) {
690
0
        *status = U_MEMORY_ALLOCATION_ERROR;
691
0
        return nullptr;
692
0
    }
693
0
    memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
694
0
    ulist_resetList(sink.values);  // Initialize the iterator.
695
0
    en->context = sink.values;
696
0
    sink.values = nullptr;  // Avoid deletion in the sink destructor.
697
0
    return en;
698
0
}
699
700
U_CAPI int32_t U_EXPORT2
701
ucol_getFunctionalEquivalent(char* result, int32_t resultCapacity,
702
                             const char* keyword, const char* locale,
703
                             UBool* isAvailable, UErrorCode* status)
704
0
{
705
    // N.B.: Resource name is "collations" but keyword is "collation"
706
0
    return ures_getFunctionalEquivalent(result, resultCapacity, U_ICUDATA_COLL,
707
0
        "collations", keyword, locale,
708
0
        isAvailable, true, status);
709
0
}
710
711
#endif /* #if !UCONFIG_NO_COLLATION */