• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 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:   US-ASCII
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
ucol_res_cleanup()72 ucol_res_cleanup() {
73     rootRules = NULL;
74     rootRulesLength = 0;
75     ures_close(rootBundle);
76     rootBundle = NULL;
77     gInitOnce.reset();
78     return TRUE;
79 }
80 
81 void U_CALLCONV
loadRootRules(UErrorCode & errorCode)82 CollationLoader::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;
91     }
92     ucln_i18n_registerCleanup(UCLN_I18N_UCOL_RES, ucol_res_cleanup);
93 }
94 
95 U_CDECL_END
96 
97 void
appendRootRules(UnicodeString & s)98 CollationLoader::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     }
104 }
105 
106 void
loadRules(const char * localeID,const char * collationType,UnicodeString & rules,UErrorCode & errorCode)107 CollationLoader::loadRules(const char *localeID, const char *collationType,
108                            UnicodeString &rules, UErrorCode &errorCode) {
109     if(U_FAILURE(errorCode)) { return; }
110     U_ASSERT(collationType != NULL && *collationType != 0);
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);
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(
125             ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
126     int32_t length;
127     const UChar *s =  ures_getStringByKey(data.getAlias(), "Sequence", &length, &errorCode);
128     if(U_FAILURE(errorCode)) { return; }
129 
130     // No string pointer aliasing so that we need not hold onto the resource bundle.
131     rules.setTo(s, length);
132     if(rules.isBogus()) {
133         errorCode = U_MEMORY_ALLOCATION_ERROR;
134     }
135 }
136 
137 template<> U_I18N_API
138 const CollationCacheEntry *
createObject(const void * creationContext,UErrorCode & errorCode) const139 LocaleCacheKey<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 
147 const CollationCacheEntry *
loadTailoring(const Locale & locale,UErrorCode & errorCode)148 CollationLoader::loadTailoring(const Locale &locale, UErrorCode &errorCode) {
149     const CollationCacheEntry *rootEntry = CollationRoot::getRootCacheEntry(errorCode);
150     if(U_FAILURE(errorCode)) { return NULL; }
151     const char *name = locale.getName();
152     if(*name == 0 || uprv_strcmp(name, "root") == 0) {
153 
154         // Have to add a ref.
155         rootEntry->addRef();
156         return rootEntry;
157     }
158 
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 
CollationLoader(const CollationCacheEntry * re,const Locale & requested,UErrorCode & errorCode)167 CollationLoader::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 
~CollationLoader()203 CollationLoader::~CollationLoader() {
204     ures_close(data);
205     ures_close(collations);
206     ures_close(bundle);
207 }
208 
209 const CollationCacheEntry *
createCacheEntry(UErrorCode & errorCode)210 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     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 
227 const CollationCacheEntry *
loadFromLocale(UErrorCode & errorCode)228 CollationLoader::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);
232     if(errorCode == U_MISSING_RESOURCE_ERROR) {
233         errorCode = U_USING_DEFAULT_WARNING;
234 
235         // Have to add that ref that we promise.
236         rootEntry->addRef();
237         return rootEntry;
238     }
239     Locale requestedLocale(locale);
240     const char *vLocale = ures_getLocaleByType(bundle, ULOC_ACTUAL_LOCALE, &errorCode);
241     if(U_FAILURE(errorCode)) { return NULL; }
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 }
252 
253 const CollationCacheEntry *
loadFromBundle(UErrorCode & errorCode)254 CollationLoader::loadFromBundle(UErrorCode &errorCode) {
255     if(U_FAILURE(errorCode)) { return NULL; }
256     U_ASSERT(collations == NULL);
257     // There are zero or more tailorings in the collations table.
258     collations = ures_getByKey(bundle, "collations", NULL, &errorCode);
259     if(errorCode == U_MISSING_RESOURCE_ERROR) {
260         errorCode = U_USING_DEFAULT_WARNING;
261         // Return the root tailoring with the validLocale, without collation type.
262         return makeCacheEntryFromRoot(validLocale, errorCode);
263     }
264     if(U_FAILURE(errorCode)) { return NULL; }
265 
266     // Fetch the default type from the data.
267     {
268         UErrorCode internalErrorCode = U_ZERO_ERROR;
269         LocalUResourceBundlePointer def(
270                 ures_getByKeyWithFallback(collations, "default", NULL, &internalErrorCode));
271         int32_t length;
272         const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
273         if(U_SUCCESS(internalErrorCode) && 0 < length && length < UPRV_LENGTHOF(defaultType)) {
274             u_UCharsToChars(s, defaultType, length + 1);
275         } else {
276             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     if(type[0] == 0) {
290         uprv_strcpy(type, defaultType);
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);
311     }
312 }
313 
314 const CollationCacheEntry *
loadFromCollations(UErrorCode & errorCode)315 CollationLoader::loadFromCollations(UErrorCode &errorCode) {
316     if(U_FAILURE(errorCode)) { return NULL; }
317     U_ASSERT(data == NULL);
318     // Load the collations/type tailoring, with type fallback.
319     LocalUResourceBundlePointer localData(
320             ures_getByKeyWithFallback(collations, type, NULL, &errorCode));
321     int32_t typeLength = uprv_strlen(type);
322     if(errorCode == U_MISSING_RESOURCE_ERROR) {
323         errorCode = U_USING_DEFAULT_WARNING;
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);
344     }
345     if(U_FAILURE(errorCode)) { return NULL; }
346 
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; }
359     }
360 
361     // Is this the same as the root collator? If so, then use that instead.
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         }
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 
380 const CollationCacheEntry *
loadFromData(UErrorCode & errorCode)381 CollationLoader::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;
387     }
388 
389     // deserialize
390     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     const uint8_t *inBytes = ures_getBinary(binary.getAlias(), &length, &errorCode);
395     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     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;
404         const UChar *s = ures_getStringByKey(data, "Sequence", &length,
405                                              &internalErrorCode);
406         if(U_SUCCESS(internalErrorCode)) {
407             t->rules.setTo(TRUE, s, length);
408         }
409     }
410 
411     const char *actualLocale = locale.getBaseName();  // without type
412     const char *vLocale = validLocale.getBaseName();
413     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     if(actualAndValidLocalesAreDifferent) {
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);
431         if(U_SUCCESS(internalErrorCode) && length < UPRV_LENGTHOF(defaultType)) {
432             u_UCharsToChars(s, defaultType, length + 1);
433         } else {
434             uprv_strcpy(defaultType, "standard");
435         }
436     }
437     t->actualLocale = locale;
438     if(uprv_strcmp(type, defaultType) != 0) {
439         t->actualLocale.setKeywordValue("collation", type, errorCode);
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);
443     }
444     if(U_FAILURE(errorCode)) { return NULL; }
445 
446     if(typeFallback) {
447         errorCode = U_USING_DEFAULT_WARNING;
448     }
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 
462 const CollationCacheEntry *
getCacheEntry(UErrorCode & errorCode)463 CollationLoader::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 
470 const CollationCacheEntry *
makeCacheEntryFromRoot(const Locale &,UErrorCode & errorCode) const471 CollationLoader::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 
481 const CollationCacheEntry *
makeCacheEntry(const Locale & loc,const CollationCacheEntry * entryFromCache,UErrorCode & errorCode)482 CollationLoader::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;
498 }
499 
500 U_NAMESPACE_END
501 
502 U_NAMESPACE_USE
503 
504 U_CAPI UCollator*
ucol_open(const char * loc,UErrorCode * status)505 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     UCollator *result = NULL;
511 
512     Collator *coll = Collator::createInstance(loc, *status);
513     if(U_SUCCESS(*status)) {
514         result = coll->toUCollator();
515     }
516     UTRACE_EXIT_PTR_STATUS(result, *status);
517     return result;
518 }
519 
520 
521 U_CAPI int32_t U_EXPORT2
ucol_getDisplayName(const char * objLoc,const char * dispLoc,UChar * result,int32_t resultLength,UErrorCode * status)522 ucol_getDisplayName(    const    char        *objLoc,
523                     const    char        *dispLoc,
524                     UChar             *result,
525                     int32_t         resultLength,
526                     UErrorCode        *status)
527 {
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);
537 }
538 
539 U_CAPI const char* U_EXPORT2
ucol_getAvailable(int32_t index)540 ucol_getAvailable(int32_t index)
541 {
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;
548 }
549 
550 U_CAPI int32_t U_EXPORT2
ucol_countAvailable()551 ucol_countAvailable()
552 {
553     int32_t count = 0;
554     Collator::getAvailableLocales(count);
555     return count;
556 }
557 
558 #if !UCONFIG_NO_SERVICE
559 U_CAPI UEnumeration* U_EXPORT2
ucol_openAvailableLocales(UErrorCode * status)560 ucol_openAvailableLocales(UErrorCode *status) {
561     // This is a wrapper over Collator::getAvailableLocales()
562     if (U_FAILURE(*status)) {
563         return NULL;
564     }
565     StringEnumeration *s = icu::Collator::getAvailableLocales();
566     if (s == NULL) {
567         *status = U_MEMORY_ALLOCATION_ERROR;
568         return NULL;
569     }
570     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
ucol_getKeywords(UErrorCode * status)583 ucol_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 
591 U_CAPI UEnumeration* U_EXPORT2
ucol_getKeywordValues(const char * keyword,UErrorCode * status)592 ucol_getKeywordValues(const char *keyword, UErrorCode *status) {
593     if (U_FAILURE(*status)) {
594         return NULL;
595     }
596     // hard-coded to accept exactly one collation keyword
597     // modify if additional collation keyword is added later
598     if (keyword==NULL || uprv_strcmp(keyword, KEYWORDS[0])!=0)
599     {
600         *status = U_ILLEGAL_ARGUMENT_ERROR;
601         return NULL;
602     }
603     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:
KeywordsSink__anon18e576630211::KeywordsSink620     KeywordsSink(UErrorCode &errorCode) :
621             values(ulist_createEmptyList(&errorCode)), hasDefault(FALSE) {}
622     virtual ~KeywordsSink();
623 
put__anon18e576630211::KeywordsSink624     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;
643                     }
644                 }
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);
648                 }
649             }
650             if (U_FAILURE(errorCode)) { return; }
651         }
652     }
653 
654     UList *values;
655     UBool hasDefault;
656 };
657 
~KeywordsSink()658 KeywordsSink::~KeywordsSink() {
659     ulist_deleteList(values);
660 }
661 
662 }  // namespace
663 
664 U_CAPI UEnumeration* U_EXPORT2
ucol_getKeywordValuesForLocale(const char *,const char * locale,UBool,UErrorCode * status)665 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     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; }
676 
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     en->context = sink.values;
684     sink.values = NULL;  // Avoid deletion in the sink destructor.
685     return en;
686 }
687 
688 U_CAPI int32_t U_EXPORT2
ucol_getFunctionalEquivalent(char * result,int32_t resultCapacity,const char * keyword,const char * locale,UBool * isAvailable,UErrorCode * status)689 ucol_getFunctionalEquivalent(char* result, int32_t resultCapacity,
690                              const char* keyword, const char* locale,
691                              UBool* isAvailable, UErrorCode* status)
692 {
693     // N.B.: Resource name is "collations" but keyword is "collation"
694     return ures_getFunctionalEquivalent(result, resultCapacity, U_ICUDATA_COLL,
695         "collations", keyword, locale,
696         isAvailable, TRUE, status);
697 }
698 
699 #endif /* #if !UCONFIG_NO_COLLATION */
700