Coverage Report

Created: 2025-06-24 06:43

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