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