Home | History | Annotate | Download | only in i18n
      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
     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
     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
     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
    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 *
    135 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 *
    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 
    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 
    199 CollationLoader::~CollationLoader() {
    200     ures_close(data);
    201     ures_close(collations);
    202     ures_close(bundle);
    203 }
    204 
    205 const CollationCacheEntry *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    467 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 *
    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*
    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
    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
    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
    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
    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
    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
    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
    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
    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