Line data Source code
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 gInitOnce = 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 : gInitOnce.reset();
78 0 : return TRUE;
79 : }
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 : }
92 0 : ucln_i18n_registerCleanup(UCLN_I18N_UCOL_RES, ucol_res_cleanup);
93 : }
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(gInitOnce, CollationLoader::loadRootRules, errorCode);
101 0 : if(U_SUCCESS(errorCode)) {
102 0 : s.append(rootRules, rootRulesLength);
103 : }
104 0 : }
105 :
106 : void
107 0 : CollationLoader::loadRules(const char *localeID, const char *collationType,
108 : 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 : char type[16];
113 0 : int32_t typeLength = uprv_strlen(collationType);
114 0 : if(typeLength >= UPRV_LENGTHOF(type)) {
115 0 : errorCode = U_ILLEGAL_ARGUMENT_ERROR;
116 0 : return;
117 : }
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 : LocalUResourceBundlePointer collations(
123 0 : ures_getByKey(bundle.getAlias(), "collations", NULL, &errorCode));
124 : LocalUResourceBundlePointer data(
125 0 : ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
126 : 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 : }
135 : }
136 :
137 : template<> U_I18N_API
138 : const CollationCacheEntry *
139 0 : LocaleCacheKey<CollationCacheEntry>::createObject(const void *creationContext,
140 : UErrorCode &errorCode) const {
141 : CollationLoader *loader =
142 : reinterpret_cast<CollationLoader *>(
143 0 : const_cast<void *>(creationContext));
144 0 : return loader->createCacheEntry(errorCode);
145 : }
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 : }
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 : }
166 :
167 0 : CollationLoader::CollationLoader(const CollationCacheEntry *re, const Locale &requested,
168 0 : UErrorCode &errorCode)
169 0 : : cache(UnifiedCache::getInstance(errorCode)), rootEntry(re),
170 : validLocale(re->validLocale), locale(requested),
171 : 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 : }
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 : } else {
196 : // Copy the collation type.
197 0 : T_CString_toLowerCase(type);
198 0 : locale.setKeywordValue("collation", type, errorCode);
199 : }
200 : }
201 : }
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 : } else {
223 0 : return loadFromData(errorCode);
224 : }
225 : }
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 : }
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 : }
246 0 : if(locale != requestedLocale) {
247 0 : return getCacheEntry(errorCode);
248 : } else {
249 0 : return loadFromBundle(errorCode);
250 : }
251 : }
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 : }
264 0 : if(U_FAILURE(errorCode)) { return NULL; }
265 :
266 : // Fetch the default type from the data.
267 : {
268 0 : UErrorCode internalErrorCode = U_ZERO_ERROR;
269 : LocalUResourceBundlePointer def(
270 0 : ures_getByKeyWithFallback(collations, "default", NULL, &internalErrorCode));
271 : 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 : } else {
276 0 : uprv_strcpy(defaultType, "standard");
277 : }
278 : }
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 : }
295 0 : if(uprv_strcmp(type, "standard") == 0) {
296 0 : typesTried |= TRIED_STANDARD;
297 : }
298 0 : locale.setKeywordValue("collation", type, errorCode);
299 0 : return getCacheEntry(errorCode);
300 : } else {
301 0 : if(uprv_strcmp(type, defaultType) == 0) {
302 0 : typesTried |= TRIED_DEFAULT;
303 : }
304 0 : if(uprv_strcmp(type, "search") == 0) {
305 0 : typesTried |= TRIED_SEARCH;
306 : }
307 0 : if(uprv_strcmp(type, "standard") == 0) {
308 0 : typesTried |= TRIED_STANDARD;
309 : }
310 0 : return loadFromCollations(errorCode);
311 : }
312 : }
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 : LocalUResourceBundlePointer localData(
320 0 : ures_getByKeyWithFallback(collations, type, NULL, &errorCode));
321 0 : int32_t typeLength = 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 : } else {
339 : // Return the root tailoring with the validLocale, without collation type.
340 0 : return makeCacheEntryFromRoot(validLocale, errorCode);
341 : }
342 0 : locale.setKeywordValue("collation", type, errorCode);
343 0 : return getCacheEntry(errorCode);
344 : }
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 = 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 0 : if(uprv_strcmp(type, defaultType) != 0) {
357 0 : validLocale.setKeywordValue("collation", type, errorCode);
358 0 : if(U_FAILURE(errorCode)) { return NULL; }
359 : }
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 : }
367 0 : return makeCacheEntryFromRoot(validLocale, errorCode);
368 : }
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 : } else {
376 0 : return loadFromData(errorCode);
377 : }
378 : }
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 : }
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 : 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 : {
402 0 : UErrorCode internalErrorCode = U_ZERO_ERROR;
403 : int32_t length;
404 0 : const UChar *s = ures_getStringByKey(data, "Sequence", &length,
405 0 : &internalErrorCode);
406 0 : if(U_SUCCESS(internalErrorCode)) {
407 0 : t->rules.setTo(TRUE, s, length);
408 : }
409 : }
410 :
411 0 : const char *actualLocale = locale.getBaseName(); // without type
412 0 : const char *vLocale = validLocale.getBaseName();
413 0 : UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0;
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 : 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 : LocalUResourceBundlePointer def(
427 0 : ures_getByKeyWithFallback(actualBundle.getAlias(), "collations/default", NULL,
428 0 : &internalErrorCode));
429 : int32_t length;
430 0 : const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
431 0 : if(U_SUCCESS(internalErrorCode) && length < UPRV_LENGTHOF(defaultType)) {
432 0 : u_UCharsToChars(s, defaultType, length + 1);
433 : } else {
434 0 : uprv_strcpy(defaultType, "standard");
435 : }
436 : }
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 : }
444 0 : if(U_FAILURE(errorCode)) { return NULL; }
445 :
446 0 : if(typeFallback) {
447 0 : errorCode = U_USING_DEFAULT_WARNING;
448 : }
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 : } else {
455 0 : t.orphan();
456 : }
457 : // Have to add that reference that we promise.
458 0 : entry->addRef();
459 0 : return entry;
460 : }
461 :
462 : const CollationCacheEntry *
463 0 : CollationLoader::getCacheEntry(UErrorCode &errorCode) {
464 0 : LocaleCacheKey<CollationCacheEntry> key(locale);
465 0 : const CollationCacheEntry *entry = NULL;
466 0 : cache->get(key, this, entry, errorCode);
467 0 : return entry;
468 : }
469 :
470 : const CollationCacheEntry *
471 0 : CollationLoader::makeCacheEntryFromRoot(
472 : const Locale &/*loc*/,
473 : UErrorCode &errorCode) const {
474 0 : if (U_FAILURE(errorCode)) {
475 0 : return NULL;
476 : }
477 0 : rootEntry->addRef();
478 0 : return makeCacheEntry(validLocale, rootEntry, errorCode);
479 : }
480 :
481 : const CollationCacheEntry *
482 0 : CollationLoader::makeCacheEntry(
483 : const Locale &loc,
484 : const CollationCacheEntry *entryFromCache,
485 : UErrorCode &errorCode) {
486 0 : if(U_FAILURE(errorCode) || loc == entryFromCache->validLocale) {
487 0 : return entryFromCache;
488 : }
489 0 : CollationCacheEntry *entry = new CollationCacheEntry(loc, entryFromCache->tailoring);
490 0 : if(entry == NULL) {
491 0 : errorCode = U_MEMORY_ALLOCATION_ERROR;
492 0 : entryFromCache->removeRef();
493 0 : return NULL;
494 : }
495 0 : entry->addRef();
496 0 : entryFromCache->removeRef();
497 0 : return entry;
498 : }
499 :
500 : U_NAMESPACE_END
501 :
502 : U_NAMESPACE_USE
503 :
504 : U_CAPI UCollator*
505 0 : ucol_open(const char *loc,
506 : UErrorCode *status)
507 : {
508 : UTRACE_ENTRY_OC(UTRACE_UCOL_OPEN);
509 : UTRACE_DATA1(UTRACE_INFO, "locale = \"%s\"", loc);
510 0 : UCollator *result = NULL;
511 :
512 0 : Collator *coll = Collator::createInstance(loc, *status);
513 0 : if(U_SUCCESS(*status)) {
514 0 : result = coll->toUCollator();
515 : }
516 : UTRACE_EXIT_PTR_STATUS(result, *status);
517 0 : return result;
518 : }
519 :
520 :
521 : U_CAPI int32_t U_EXPORT2
522 0 : ucol_getDisplayName( const char *objLoc,
523 : const char *dispLoc,
524 : UChar *result,
525 : int32_t resultLength,
526 : UErrorCode *status)
527 : {
528 0 : if(U_FAILURE(*status)) return -1;
529 0 : UnicodeString dst;
530 0 : if(!(result==NULL && resultLength==0)) {
531 : // NULL destination for pure preflighting: empty dummy string
532 : // otherwise, alias the destination buffer
533 0 : dst.setTo(result, 0, resultLength);
534 : }
535 0 : Collator::getDisplayName(Locale(objLoc), Locale(dispLoc), dst);
536 0 : return dst.extract(result, resultLength, *status);
537 : }
538 :
539 : U_CAPI const char* U_EXPORT2
540 0 : ucol_getAvailable(int32_t index)
541 : {
542 0 : int32_t count = 0;
543 0 : const Locale *loc = Collator::getAvailableLocales(count);
544 0 : if (loc != NULL && index < count) {
545 0 : return loc[index].getName();
546 : }
547 0 : return NULL;
548 : }
549 :
550 : U_CAPI int32_t U_EXPORT2
551 0 : ucol_countAvailable()
552 : {
553 0 : int32_t count = 0;
554 0 : Collator::getAvailableLocales(count);
555 0 : return count;
556 : }
557 :
558 : #if !UCONFIG_NO_SERVICE
559 : U_CAPI UEnumeration* U_EXPORT2
560 0 : ucol_openAvailableLocales(UErrorCode *status) {
561 : // This is a wrapper over Collator::getAvailableLocales()
562 0 : if (U_FAILURE(*status)) {
563 0 : return NULL;
564 : }
565 0 : StringEnumeration *s = icu::Collator::getAvailableLocales();
566 0 : if (s == NULL) {
567 0 : *status = U_MEMORY_ALLOCATION_ERROR;
568 0 : return NULL;
569 : }
570 0 : return uenum_openFromStringEnumeration(s, status);
571 : }
572 : #endif
573 :
574 : // Note: KEYWORDS[0] != RESOURCE_NAME - alan
575 :
576 : static const char RESOURCE_NAME[] = "collations";
577 :
578 : static const char* const KEYWORDS[] = { "collation" };
579 :
580 : #define KEYWORD_COUNT UPRV_LENGTHOF(KEYWORDS)
581 :
582 : U_CAPI UEnumeration* U_EXPORT2
583 0 : ucol_getKeywords(UErrorCode *status) {
584 0 : UEnumeration *result = NULL;
585 0 : if (U_SUCCESS(*status)) {
586 0 : return uenum_openCharStringsEnumeration(KEYWORDS, KEYWORD_COUNT, status);
587 : }
588 0 : return result;
589 : }
590 :
591 : U_CAPI UEnumeration* U_EXPORT2
592 0 : ucol_getKeywordValues(const char *keyword, UErrorCode *status) {
593 0 : if (U_FAILURE(*status)) {
594 0 : return NULL;
595 : }
596 : // hard-coded to accept exactly one collation keyword
597 : // modify if additional collation keyword is added later
598 0 : if (keyword==NULL || uprv_strcmp(keyword, KEYWORDS[0])!=0)
599 : {
600 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
601 0 : return NULL;
602 : }
603 0 : return ures_getKeywordValues(U_ICUDATA_COLL, RESOURCE_NAME, status);
604 : }
605 :
606 : static 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 :
616 : namespace {
617 :
618 : struct KeywordsSink : public ResourceSink {
619 : public:
620 0 : KeywordsSink(UErrorCode &errorCode) :
621 0 : values(ulist_createEmptyList(&errorCode)), hasDefault(FALSE) {}
622 : virtual ~KeywordsSink();
623 :
624 0 : virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
625 : UErrorCode &errorCode) {
626 0 : if (U_FAILURE(errorCode)) { return; }
627 0 : ResourceTable collations = value.getTable(errorCode);
628 0 : for (int32_t i = 0; collations.getKeyAndValue(i, key, value); ++i) {
629 0 : UResType type = value.getType();
630 0 : if (type == URES_STRING) {
631 0 : if (!hasDefault && uprv_strcmp(key, "default") == 0) {
632 0 : CharString defcoll;
633 0 : defcoll.appendInvariantChars(value.getUnicodeString(errorCode), errorCode);
634 0 : if (U_SUCCESS(errorCode) && !defcoll.isEmpty()) {
635 0 : char *ownedDefault = uprv_strdup(defcoll.data());
636 0 : if (ownedDefault == NULL) {
637 0 : errorCode = U_MEMORY_ALLOCATION_ERROR;
638 0 : return;
639 : }
640 0 : ulist_removeString(values, defcoll.data());
641 0 : ulist_addItemBeginList(values, ownedDefault, TRUE, &errorCode);
642 0 : hasDefault = TRUE;
643 : }
644 : }
645 0 : } else if (type == URES_TABLE && uprv_strncmp(key, "private-", 8) != 0) {
646 0 : if (!ulist_containsString(values, key, (int32_t)uprv_strlen(key))) {
647 0 : ulist_addItemEndList(values, key, FALSE, &errorCode);
648 : }
649 : }
650 0 : if (U_FAILURE(errorCode)) { return; }
651 : }
652 : }
653 :
654 : UList *values;
655 : UBool hasDefault;
656 : };
657 :
658 0 : KeywordsSink::~KeywordsSink() {
659 0 : ulist_deleteList(values);
660 0 : }
661 :
662 : } // namespace
663 :
664 : U_CAPI UEnumeration* U_EXPORT2
665 0 : ucol_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 0 : LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_COLL, locale, status));
673 0 : KeywordsSink sink(*status);
674 0 : ures_getAllItemsWithFallback(bundle.getAlias(), RESOURCE_NAME, sink, *status);
675 0 : if (U_FAILURE(*status)) { return NULL; }
676 :
677 0 : UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
678 0 : if (en == NULL) {
679 0 : *status = U_MEMORY_ALLOCATION_ERROR;
680 0 : return NULL;
681 : }
682 0 : memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
683 0 : ulist_resetList(sink.values); // Initialize the iterator.
684 0 : en->context = sink.values;
685 0 : sink.values = NULL; // Avoid deletion in the sink destructor.
686 0 : return en;
687 : }
688 :
689 : U_CAPI int32_t U_EXPORT2
690 0 : ucol_getFunctionalEquivalent(char* result, int32_t resultCapacity,
691 : const char* keyword, const char* locale,
692 : UBool* isAvailable, UErrorCode* status)
693 : {
694 : // N.B.: Resource name is "collations" but keyword is "collation"
695 : return ures_getFunctionalEquivalent(result, resultCapacity, U_ICUDATA_COLL,
696 : "collations", keyword, locale,
697 0 : isAvailable, TRUE, status);
698 : }
699 :
700 : #endif /* #if !UCONFIG_NO_COLLATION */
|