]>
Commit | Line | Data |
---|---|---|
73c04bcf A |
1 | /* |
2 | ******************************************************************************* | |
57a6839d | 3 | * Copyright (C) 1996-2014, International Business Machines |
73c04bcf A |
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 | |
57a6839d | 22 | * 2012-2014 markus Rewritten in C++ again. |
73c04bcf A |
23 | */ |
24 | ||
25 | #include "unicode/utypes.h" | |
26 | ||
27 | #if !UCONFIG_NO_COLLATION | |
57a6839d | 28 | |
73c04bcf | 29 | #include "unicode/coll.h" |
57a6839d A |
30 | #include "unicode/localpointer.h" |
31 | #include "unicode/locid.h" | |
73c04bcf | 32 | #include "unicode/tblcoll.h" |
57a6839d A |
33 | #include "unicode/ucol.h" |
34 | #include "unicode/uloc.h" | |
35 | #include "unicode/unistr.h" | |
36 | #include "unicode/ures.h" | |
37 | #include "cmemory.h" | |
73c04bcf | 38 | #include "cstring.h" |
57a6839d A |
39 | #include "collationdatareader.h" |
40 | #include "collationroot.h" | |
41 | #include "collationtailoring.h" | |
73c04bcf | 42 | #include "putilimp.h" |
57a6839d A |
43 | #include "uassert.h" |
44 | #include "ucln_in.h" | |
45 | #include "ucol_imp.h" | |
729e4ab9 A |
46 | #include "uenumimp.h" |
47 | #include "ulist.h" | |
57a6839d | 48 | #include "umutex.h" |
b331163b | 49 | #include "unifiedcache.h" |
57a6839d A |
50 | #include "uresimp.h" |
51 | #include "ustrenum.h" | |
52 | #include "utracimp.h" | |
73c04bcf | 53 | |
57a6839d | 54 | U_NAMESPACE_BEGIN |
729e4ab9 | 55 | |
57a6839d A |
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 | |
46f4442e | 64 | |
73c04bcf | 65 | U_CDECL_BEGIN |
57a6839d | 66 | |
46f4442e | 67 | static UBool U_CALLCONV |
57a6839d A |
68 | ucol_res_cleanup() { |
69 | rootRules = NULL; | |
70 | rootRulesLength = 0; | |
71 | ures_close(rootBundle); | |
72 | rootBundle = NULL; | |
73 | gInitOnce.reset(); | |
46f4442e A |
74 | return TRUE; |
75 | } | |
76 | ||
73c04bcf A |
77 | U_CDECL_END |
78 | ||
57a6839d A |
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; | |
46f4442e | 89 | } |
57a6839d | 90 | ucln_i18n_registerCleanup(UCLN_I18N_UCOL_RES, ucol_res_cleanup); |
46f4442e A |
91 | } |
92 | ||
57a6839d A |
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 | } | |
46f4442e A |
100 | } |
101 | ||
b331163b A |
102 | void |
103 | CollationLoader::loadRules(const char *localeID, const char *collationType, | |
104 | UnicodeString &rules, UErrorCode &errorCode) { | |
105 | if(U_FAILURE(errorCode)) { return; } | |
57a6839d | 106 | U_ASSERT(collationType != NULL && *collationType != 0); |
b331163b A |
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); | |
57a6839d A |
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( | |
b331163b | 121 | ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode)); |
57a6839d A |
122 | int32_t length; |
123 | const UChar *s = ures_getStringByKey(data.getAlias(), "Sequence", &length, &errorCode); | |
b331163b | 124 | if(U_FAILURE(errorCode)) { return; } |
57a6839d A |
125 | |
126 | // No string pointer aliasing so that we need not hold onto the resource bundle. | |
b331163b A |
127 | rules.setTo(s, length); |
128 | if(rules.isBogus()) { | |
57a6839d | 129 | errorCode = U_MEMORY_ALLOCATION_ERROR; |
57a6839d | 130 | } |
73c04bcf A |
131 | } |
132 | ||
b331163b A |
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); | |
57a6839d A |
146 | if(U_FAILURE(errorCode)) { return NULL; } |
147 | const char *name = locale.getName(); | |
148 | if(*name == 0 || uprv_strcmp(name, "root") == 0) { | |
b331163b A |
149 | |
150 | // Have to add a ref. | |
151 | rootEntry->addRef(); | |
152 | return rootEntry; | |
57a6839d A |
153 | } |
154 | ||
b331163b A |
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); | |
57a6839d A |
228 | if(errorCode == U_MISSING_RESOURCE_ERROR) { |
229 | errorCode = U_USING_DEFAULT_WARNING; | |
b331163b A |
230 | |
231 | // Have to add that ref that we promise. | |
232 | rootEntry->addRef(); | |
233 | return rootEntry; | |
57a6839d | 234 | } |
b331163b A |
235 | Locale requestedLocale(locale); |
236 | const char *vLocale = ures_getLocaleByType(bundle, ULOC_ACTUAL_LOCALE, &errorCode); | |
57a6839d | 237 | if(U_FAILURE(errorCode)) { return NULL; } |
b331163b A |
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 | } | |
57a6839d | 248 | |
b331163b A |
249 | const CollationCacheEntry * |
250 | CollationLoader::loadFromBundle(UErrorCode &errorCode) { | |
251 | if(U_FAILURE(errorCode)) { return NULL; } | |
252 | U_ASSERT(collations == NULL); | |
57a6839d | 253 | // There are zero or more tailorings in the collations table. |
b331163b | 254 | collations = ures_getByKey(bundle, "collations", NULL, &errorCode); |
57a6839d A |
255 | if(errorCode == U_MISSING_RESOURCE_ERROR) { |
256 | errorCode = U_USING_DEFAULT_WARNING; | |
b331163b A |
257 | // Return the root tailoring with the validLocale, without collation type. |
258 | return makeCacheEntryFromRoot(validLocale, errorCode); | |
57a6839d A |
259 | } |
260 | if(U_FAILURE(errorCode)) { return NULL; } | |
261 | ||
b331163b | 262 | // Fetch the default type from the data. |
57a6839d A |
263 | { |
264 | UErrorCode internalErrorCode = U_ZERO_ERROR; | |
265 | LocalUResourceBundlePointer def( | |
b331163b | 266 | ures_getByKeyWithFallback(collations, "default", NULL, &internalErrorCode)); |
57a6839d A |
267 | int32_t length; |
268 | const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode); | |
b331163b | 269 | if(U_SUCCESS(internalErrorCode) && 0 < length && length < UPRV_LENGTHOF(defaultType)) { |
57a6839d | 270 | u_UCharsToChars(s, defaultType, length + 1); |
73c04bcf | 271 | } else { |
57a6839d A |
272 | uprv_strcpy(defaultType, "standard"); |
273 | } | |
274 | } | |
b331163b A |
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) { | |
57a6839d | 286 | uprv_strcpy(type, defaultType); |
b331163b A |
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); | |
57a6839d | 307 | } |
b331163b | 308 | } |
57a6839d | 309 | |
b331163b A |
310 | const CollationCacheEntry * |
311 | CollationLoader::loadFromCollations(UErrorCode &errorCode) { | |
312 | if(U_FAILURE(errorCode)) { return NULL; } | |
313 | U_ASSERT(data == NULL); | |
57a6839d | 314 | // Load the collations/type tailoring, with type fallback. |
b331163b A |
315 | LocalUResourceBundlePointer localData( |
316 | ures_getByKeyWithFallback(collations, type, NULL, &errorCode)); | |
317 | int32_t typeLength = uprv_strlen(type); | |
57a6839d A |
318 | if(errorCode == U_MISSING_RESOURCE_ERROR) { |
319 | errorCode = U_USING_DEFAULT_WARNING; | |
b331163b A |
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); | |
57a6839d A |
340 | } |
341 | if(U_FAILURE(errorCode)) { return NULL; } | |
342 | ||
b331163b A |
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; } | |
73c04bcf | 355 | } |
73c04bcf | 356 | |
57a6839d | 357 | // Is this the same as the root collator? If so, then use that instead. |
57a6839d A |
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 | } | |
b331163b A |
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; | |
57a6839d | 383 | } |
57a6839d A |
384 | |
385 | // deserialize | |
b331163b | 386 | LocalUResourceBundlePointer binary(ures_getByKey(data, "%%CollationBin", NULL, &errorCode)); |
57a6839d A |
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); | |
b331163b | 391 | CollationDataReader::read(rootEntry->tailoring, inBytes, length, *t, errorCode); |
57a6839d A |
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; | |
b331163b | 400 | const UChar *s = ures_getStringByKey(data, "Sequence", &length, |
57a6839d | 401 | &internalErrorCode); |
b331163b | 402 | if(U_SUCCESS(internalErrorCode)) { |
57a6839d A |
403 | t->rules.setTo(TRUE, s, length); |
404 | } | |
405 | } | |
406 | ||
b331163b A |
407 | const char *actualLocale = locale.getBaseName(); // without type |
408 | const char *vLocale = validLocale.getBaseName(); | |
409 | UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0; | |
57a6839d A |
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. | |
b331163b | 416 | if(actualAndValidLocalesAreDifferent) { |
57a6839d A |
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); | |
b331163b | 427 | if(U_SUCCESS(internalErrorCode) && length < UPRV_LENGTHOF(defaultType)) { |
57a6839d A |
428 | u_UCharsToChars(s, defaultType, length + 1); |
429 | } else { | |
430 | uprv_strcpy(defaultType, "standard"); | |
73c04bcf | 431 | } |
73c04bcf | 432 | } |
b331163b | 433 | t->actualLocale = locale; |
57a6839d A |
434 | if(uprv_strcmp(type, defaultType) != 0) { |
435 | t->actualLocale.setKeywordValue("collation", type, errorCode); | |
b331163b A |
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); | |
73c04bcf | 439 | } |
b331163b | 440 | if(U_FAILURE(errorCode)) { return NULL; } |
57a6839d A |
441 | |
442 | if(typeFallback) { | |
443 | errorCode = U_USING_DEFAULT_WARNING; | |
46f4442e | 444 | } |
b331163b A |
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; | |
57a6839d | 494 | } |
73c04bcf | 495 | |
57a6839d | 496 | U_NAMESPACE_END |
73c04bcf | 497 | |
57a6839d | 498 | U_NAMESPACE_USE |
73c04bcf A |
499 | |
500 | U_CAPI UCollator* | |
501 | ucol_open(const char *loc, | |
502 | UErrorCode *status) | |
503 | { | |
46f4442e A |
504 | U_NAMESPACE_USE |
505 | ||
506 | UTRACE_ENTRY_OC(UTRACE_UCOL_OPEN); | |
507 | UTRACE_DATA1(UTRACE_INFO, "locale = \"%s\"", loc); | |
508 | UCollator *result = NULL; | |
73c04bcf | 509 | |
57a6839d | 510 | Collator *coll = Collator::createInstance(loc, *status); |
46f4442e | 511 | if(U_SUCCESS(*status)) { |
57a6839d | 512 | result = coll->toUCollator(); |
73c04bcf | 513 | } |
57a6839d | 514 | UTRACE_EXIT_PTR_STATUS(result, *status); |
46f4442e | 515 | return result; |
73c04bcf A |
516 | } |
517 | ||
73c04bcf A |
518 | |
519 | U_CAPI int32_t U_EXPORT2 | |
520 | ucol_getDisplayName( const char *objLoc, | |
46f4442e A |
521 | const char *dispLoc, |
522 | UChar *result, | |
523 | int32_t resultLength, | |
524 | UErrorCode *status) | |
73c04bcf | 525 | { |
46f4442e A |
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); | |
73c04bcf A |
537 | } |
538 | ||
539 | U_CAPI const char* U_EXPORT2 | |
540 | ucol_getAvailable(int32_t index) | |
541 | { | |
46f4442e A |
542 | int32_t count = 0; |
543 | const Locale *loc = Collator::getAvailableLocales(count); | |
544 | if (loc != NULL && index < count) { | |
545 | return loc[index].getName(); | |
546 | } | |
547 | return NULL; | |
73c04bcf A |
548 | } |
549 | ||
550 | U_CAPI int32_t U_EXPORT2 | |
551 | ucol_countAvailable() | |
552 | { | |
46f4442e A |
553 | int32_t count = 0; |
554 | Collator::getAvailableLocales(count); | |
555 | return count; | |
73c04bcf A |
556 | } |
557 | ||
558 | #if !UCONFIG_NO_SERVICE | |
559 | U_CAPI UEnumeration* U_EXPORT2 | |
560 | ucol_openAvailableLocales(UErrorCode *status) { | |
46f4442e A |
561 | U_NAMESPACE_USE |
562 | ||
73c04bcf A |
563 | // This is a wrapper over Collator::getAvailableLocales() |
564 | if (U_FAILURE(*status)) { | |
565 | return NULL; | |
566 | } | |
4388f060 | 567 | StringEnumeration *s = icu::Collator::getAvailableLocales(); |
73c04bcf A |
568 | if (s == NULL) { |
569 | *status = U_MEMORY_ALLOCATION_ERROR; | |
570 | return NULL; | |
571 | } | |
729e4ab9 | 572 | return uenum_openFromStringEnumeration(s, status); |
73c04bcf A |
573 | } |
574 | #endif | |
575 | ||
576 | // Note: KEYWORDS[0] != RESOURCE_NAME - alan | |
577 | ||
46f4442e | 578 | static const char RESOURCE_NAME[] = "collations"; |
73c04bcf | 579 | |
46f4442e | 580 | static const char* const KEYWORDS[] = { "collation" }; |
73c04bcf | 581 | |
b331163b | 582 | #define KEYWORD_COUNT UPRV_LENGTHOF(KEYWORDS) |
73c04bcf A |
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) { | |
46f4442e A |
595 | if (U_FAILURE(*status)) { |
596 | return NULL; | |
597 | } | |
73c04bcf A |
598 | // hard-coded to accept exactly one collation keyword |
599 | // modify if additional collation keyword is added later | |
46f4442e A |
600 | if (keyword==NULL || uprv_strcmp(keyword, KEYWORDS[0])!=0) |
601 | { | |
73c04bcf A |
602 | *status = U_ILLEGAL_ARGUMENT_ERROR; |
603 | return NULL; | |
604 | } | |
605 | return ures_getKeywordValues(U_ICUDATA_COLL, RESOURCE_NAME, status); | |
606 | } | |
607 | ||
729e4ab9 A |
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 | } | |
b331163b | 690 | } else if (uprv_strncmp(key, "private-", 8) != 0) { |
729e4ab9 A |
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 | ||
73c04bcf A |
734 | U_CAPI int32_t U_EXPORT2 |
735 | ucol_getFunctionalEquivalent(char* result, int32_t resultCapacity, | |
736 | const char* keyword, const char* locale, | |
46f4442e A |
737 | UBool* isAvailable, UErrorCode* status) |
738 | { | |
73c04bcf A |
739 | // N.B.: Resource name is "collations" but keyword is "collation" |
740 | return ures_getFunctionalEquivalent(result, resultCapacity, U_ICUDATA_COLL, | |
46f4442e A |
741 | "collations", keyword, locale, |
742 | isAvailable, TRUE, status); | |
73c04bcf A |
743 | } |
744 | ||
73c04bcf | 745 | #endif /* #if !UCONFIG_NO_COLLATION */ |