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