2 **********************************************************************
3 * Copyright (C) 2014, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
7 #include "unicode/utypes.h"
17 static UHashtable
* gLocExtKeyMap
= NULL
;
18 static icu::UInitOnce gLocExtKeyMapInitOnce
= U_INITONCE_INITIALIZER
;
19 static icu::UVector
* gKeyTypeStringPool
= NULL
;
20 static icu::UVector
* gLocExtKeyDataEntries
= NULL
;
21 static icu::UVector
* gLocExtTypeEntries
= NULL
;
23 // bit flags for special types
26 SPECIALTYPE_CODEPOINTS
= 1,
27 SPECIALTYPE_REORDER_CODE
= 2
30 typedef struct LocExtKeyData
{
34 uint32_t specialTypes
;
37 typedef struct LocExtType
{
44 static UBool U_CALLCONV
45 uloc_key_type_cleanup(void) {
46 if (gLocExtKeyMap
!= NULL
) {
47 uhash_close(gLocExtKeyMap
);
51 delete gLocExtKeyDataEntries
;
52 gLocExtKeyDataEntries
= NULL
;
54 delete gLocExtTypeEntries
;
55 gLocExtTypeEntries
= NULL
;
57 delete gKeyTypeStringPool
;
58 gKeyTypeStringPool
= NULL
;
60 gLocExtKeyMapInitOnce
.reset();
64 static void U_CALLCONV
65 uloc_deleteKeyTypeStringPoolEntry(void* obj
) {
69 static void U_CALLCONV
70 uloc_deleteKeyDataEntry(void* obj
) {
71 LocExtKeyData
* keyData
= (LocExtKeyData
*)obj
;
72 if (keyData
->typeMap
!= NULL
) {
73 uhash_close(keyData
->typeMap
);
78 static void U_CALLCONV
79 uloc_deleteTypeEntry(void* obj
) {
86 static void U_CALLCONV
87 initFromResourceBundle(UErrorCode
& sts
) {
89 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE
, uloc_key_type_cleanup
);
91 gLocExtKeyMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
93 LocalUResourceBundlePointer
keyTypeDataRes(ures_openDirect(NULL
, "keyTypeData", &sts
));
94 LocalUResourceBundlePointer
keyMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "keyMap", NULL
, &sts
));
95 LocalUResourceBundlePointer
typeMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeMap", NULL
, &sts
));
101 UErrorCode tmpSts
= U_ZERO_ERROR
;
102 LocalUResourceBundlePointer
typeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeAlias", NULL
, &tmpSts
));
103 tmpSts
= U_ZERO_ERROR
;
104 LocalUResourceBundlePointer
bcpTypeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "bcpTypeAlias", NULL
, &tmpSts
));
106 // initialize vectors storing dynamically allocated objects
107 gKeyTypeStringPool
= new UVector(uloc_deleteKeyTypeStringPoolEntry
, NULL
, sts
);
108 if (gKeyTypeStringPool
== NULL
) {
109 if (U_SUCCESS(sts
)) {
110 sts
= U_MEMORY_ALLOCATION_ERROR
;
113 if (U_FAILURE(sts
)) {
116 gLocExtKeyDataEntries
= new UVector(uloc_deleteKeyDataEntry
, NULL
, sts
);
117 if (gLocExtKeyDataEntries
== NULL
) {
118 if (U_SUCCESS(sts
)) {
119 sts
= U_MEMORY_ALLOCATION_ERROR
;
122 if (U_FAILURE(sts
)) {
125 gLocExtTypeEntries
= new UVector(uloc_deleteTypeEntry
, NULL
, sts
);
126 if (gLocExtTypeEntries
== NULL
) {
127 if (U_SUCCESS(sts
)) {
128 sts
= U_MEMORY_ALLOCATION_ERROR
;
131 if (U_FAILURE(sts
)) {
135 // iterate through keyMap resource
136 LocalUResourceBundlePointer keyMapEntry
;
138 while (ures_hasNext(keyMapRes
.getAlias())) {
139 keyMapEntry
.adoptInstead(ures_getNextResource(keyMapRes
.getAlias(), keyMapEntry
.orphan(), &sts
));
140 if (U_FAILURE(sts
)) {
143 const char* legacyKeyId
= ures_getKey(keyMapEntry
.getAlias());
144 int32_t bcpKeyIdLen
= 0;
145 const UChar
* uBcpKeyId
= ures_getString(keyMapEntry
.getAlias(), &bcpKeyIdLen
, &sts
);
146 if (U_FAILURE(sts
)) {
150 // empty value indicates that BCP key is same with the legacy key.
151 const char* bcpKeyId
= legacyKeyId
;
152 if (bcpKeyIdLen
> 0) {
153 char* bcpKeyIdBuf
= (char*)uprv_malloc(bcpKeyIdLen
+ 1);
154 if (bcpKeyIdBuf
== NULL
) {
155 sts
= U_MEMORY_ALLOCATION_ERROR
;
158 u_UCharsToChars(uBcpKeyId
, bcpKeyIdBuf
, bcpKeyIdLen
);
159 bcpKeyIdBuf
[bcpKeyIdLen
] = 0;
160 gKeyTypeStringPool
->addElement(bcpKeyIdBuf
, sts
);
161 if (U_FAILURE(sts
)) {
164 bcpKeyId
= bcpKeyIdBuf
;
167 UBool isTZ
= uprv_strcmp(legacyKeyId
, "timezone") == 0;
169 UHashtable
* typeDataMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
170 if (U_FAILURE(sts
)) {
173 uint32_t specialTypes
= SPECIALTYPE_NONE
;
175 LocalUResourceBundlePointer typeAliasResByKey
;
176 LocalUResourceBundlePointer bcpTypeAliasResByKey
;
178 if (typeAliasRes
.isValid()) {
179 tmpSts
= U_ZERO_ERROR
;
180 typeAliasResByKey
.adoptInstead(ures_getByKey(typeAliasRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
181 if (U_FAILURE(tmpSts
)) {
182 typeAliasResByKey
.orphan();
185 if (bcpTypeAliasRes
.isValid()) {
186 tmpSts
= U_ZERO_ERROR
;
187 bcpTypeAliasResByKey
.adoptInstead(ures_getByKey(bcpTypeAliasRes
.getAlias(), bcpKeyId
, NULL
, &tmpSts
));
188 if (U_FAILURE(tmpSts
)) {
189 bcpTypeAliasResByKey
.orphan();
193 // look up type map for the key, and walk through the mapping data
194 tmpSts
= U_ZERO_ERROR
;
195 LocalUResourceBundlePointer
typeMapResByKey(ures_getByKey(typeMapRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
196 if (U_FAILURE(tmpSts
)) {
197 // type map for each key must exist
200 LocalUResourceBundlePointer typeMapEntry
;
202 while (ures_hasNext(typeMapResByKey
.getAlias())) {
203 typeMapEntry
.adoptInstead(ures_getNextResource(typeMapResByKey
.getAlias(), typeMapEntry
.orphan(), &sts
));
204 if (U_FAILURE(sts
)) {
207 const char* legacyTypeId
= ures_getKey(typeMapEntry
.getAlias());
210 if (uprv_strcmp(legacyTypeId
, "CODEPOINTS") == 0) {
211 specialTypes
|= SPECIALTYPE_CODEPOINTS
;
214 if (uprv_strcmp(legacyTypeId
, "REORDER_CODE") == 0) {
215 specialTypes
|= SPECIALTYPE_REORDER_CODE
;
220 // a timezone key uses a colon instead of a slash in the resource.
221 // e.g. America:Los_Angeles
222 if (uprv_strchr(legacyTypeId
, ':') != NULL
) {
223 int32_t legacyTypeIdLen
= uprv_strlen(legacyTypeId
);
224 char* legacyTypeIdBuf
= (char*)uprv_malloc(legacyTypeIdLen
+ 1);
225 if (legacyTypeIdBuf
== NULL
) {
226 sts
= U_MEMORY_ALLOCATION_ERROR
;
229 const char* p
= legacyTypeId
;
230 char* q
= legacyTypeIdBuf
;
241 gKeyTypeStringPool
->addElement(legacyTypeIdBuf
, sts
);
242 if (U_FAILURE(sts
)) {
245 legacyTypeId
= legacyTypeIdBuf
;
249 int32_t bcpTypeIdLen
= 0;
250 const UChar
* uBcpTypeId
= ures_getString(typeMapEntry
.getAlias(), &bcpTypeIdLen
, &sts
);
251 if (U_FAILURE(sts
)) {
255 // empty value indicates that BCP type is same with the legacy type.
256 const char* bcpTypeId
= legacyTypeId
;
257 if (bcpTypeIdLen
> 0) {
258 char* bcpTypeIdBuf
= (char*)uprv_malloc(bcpTypeIdLen
+ 1);
259 if (bcpTypeIdBuf
== NULL
) {
260 sts
= U_MEMORY_ALLOCATION_ERROR
;
263 u_UCharsToChars(uBcpTypeId
, bcpTypeIdBuf
, bcpTypeIdLen
);
264 bcpTypeIdBuf
[bcpTypeIdLen
] = 0;
265 gKeyTypeStringPool
->addElement(bcpTypeIdBuf
, sts
);
266 if (U_FAILURE(sts
)) {
269 bcpTypeId
= bcpTypeIdBuf
;
272 // Note: legacy type value should never be
273 // equivalent to bcp type value of a different
274 // type under the same key. So we use a single
276 LocExtType
* t
= (LocExtType
*)uprv_malloc(sizeof(LocExtType
));
278 sts
= U_MEMORY_ALLOCATION_ERROR
;
281 t
->bcpId
= bcpTypeId
;
282 t
->legacyId
= legacyTypeId
;
283 gLocExtTypeEntries
->addElement((void*)t
, sts
);
284 if (U_FAILURE(sts
)) {
288 uhash_put(typeDataMap
, (void*)legacyTypeId
, t
, &sts
);
289 if (bcpTypeId
!= legacyTypeId
) {
290 // different type value
291 uhash_put(typeDataMap
, (void*)bcpTypeId
, t
, &sts
);
293 if (U_FAILURE(sts
)) {
297 // also put aliases in the map
298 if (typeAliasResByKey
.isValid()) {
299 LocalUResourceBundlePointer typeAliasDataEntry
;
301 ures_resetIterator(typeAliasResByKey
.getAlias());
302 while (ures_hasNext(typeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
304 typeAliasDataEntry
.adoptInstead(ures_getNextResource(typeAliasResByKey
.getAlias(), typeAliasDataEntry
.orphan(), &sts
));
305 const UChar
* to
= ures_getString(typeAliasDataEntry
.getAlias(), &toLen
, &sts
);
306 if (U_FAILURE(sts
)) {
309 // check if this is an alias of canoncal legacy type
310 if (uprv_compareInvAscii(NULL
, legacyTypeId
, -1, to
, toLen
) == 0) {
311 const char* from
= ures_getKey(typeAliasDataEntry
.getAlias());
313 // replace colon with slash if necessary
314 if (uprv_strchr(from
, ':') != NULL
) {
315 int32_t fromLen
= uprv_strlen(from
);
316 char* fromBuf
= (char*)uprv_malloc(fromLen
+ 1);
317 if (fromBuf
== NULL
) {
318 sts
= U_MEMORY_ALLOCATION_ERROR
;
321 const char* p
= from
;
333 gKeyTypeStringPool
->addElement(fromBuf
, sts
);
334 if (U_FAILURE(sts
)) {
340 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
343 if (U_FAILURE(sts
)) {
348 if (bcpTypeAliasResByKey
.isValid()) {
349 LocalUResourceBundlePointer bcpTypeAliasDataEntry
;
351 ures_resetIterator(bcpTypeAliasResByKey
.getAlias());
352 while (ures_hasNext(bcpTypeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
354 bcpTypeAliasDataEntry
.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey
.getAlias(), bcpTypeAliasDataEntry
.orphan(), &sts
));
355 const UChar
* to
= ures_getString(bcpTypeAliasDataEntry
.getAlias(), &toLen
, &sts
);
356 if (U_FAILURE(sts
)) {
359 // check if this is an alias of bcp type
360 if (uprv_compareInvAscii(NULL
, bcpTypeId
, -1, to
, toLen
) == 0) {
361 const char* from
= ures_getKey(bcpTypeAliasDataEntry
.getAlias());
362 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
365 if (U_FAILURE(sts
)) {
371 if (U_FAILURE(sts
)) {
375 LocExtKeyData
* keyData
= (LocExtKeyData
*)uprv_malloc(sizeof(LocExtKeyData
));
376 if (keyData
== NULL
) {
377 sts
= U_MEMORY_ALLOCATION_ERROR
;
380 keyData
->bcpId
= bcpKeyId
;
381 keyData
->legacyId
= legacyKeyId
;
382 keyData
->specialTypes
= specialTypes
;
383 keyData
->typeMap
= typeDataMap
;
385 gLocExtKeyDataEntries
->addElement((void*)keyData
, sts
);
386 if (U_FAILURE(sts
)) {
390 uhash_put(gLocExtKeyMap
, (void*)legacyKeyId
, keyData
, &sts
);
391 if (legacyKeyId
!= bcpKeyId
) {
392 // different key value
393 uhash_put(gLocExtKeyMap
, (void*)bcpKeyId
, keyData
, &sts
);
395 if (U_FAILURE(sts
)) {
403 UErrorCode sts
= U_ZERO_ERROR
;
404 umtx_initOnce(gLocExtKeyMapInitOnce
, &initFromResourceBundle
, sts
);
405 if (U_FAILURE(sts
)) {
412 isSpecialTypeCodepoints(const char* val
) {
413 int32_t subtagLen
= 0;
417 if (subtagLen
< 4 || subtagLen
> 6) {
421 } else if ((*p
>= '0' && *p
<= '9') ||
422 (*p
>= 'A' && *p
<= 'F') || // A-F/a-f are contiguous
423 (*p
>= 'a' && *p
<= 'f')) { // also in EBCDIC
430 return (subtagLen
>= 4 && subtagLen
<= 6);
434 isSpecialTypeReorderCode(const char* val
) {
435 int32_t subtagLen
= 0;
439 if (subtagLen
< 3 || subtagLen
> 8) {
443 } else if (uprv_isASCIILetter(*p
)) {
450 return (subtagLen
>=3 && subtagLen
<=8);
454 ulocimp_toBcpKey(const char* key
) {
459 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
460 if (keyData
!= NULL
) {
461 return keyData
->bcpId
;
467 ulocimp_toLegacyKey(const char* key
) {
472 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
473 if (keyData
!= NULL
) {
474 return keyData
->legacyId
;
480 ulocimp_toBcpType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
481 if (isKnownKey
!= NULL
) {
484 if (isSpecialType
!= NULL
) {
485 *isSpecialType
= FALSE
;
492 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
493 if (keyData
!= NULL
) {
494 if (isKnownKey
!= NULL
) {
497 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
501 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
502 UBool matched
= FALSE
;
503 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
504 matched
= isSpecialTypeCodepoints(type
);
506 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
507 matched
= isSpecialTypeReorderCode(type
);
510 if (isSpecialType
!= NULL
) {
511 *isSpecialType
= TRUE
;
522 ulocimp_toLegacyType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
523 if (isKnownKey
!= NULL
) {
526 if (isSpecialType
!= NULL
) {
527 *isSpecialType
= FALSE
;
534 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
535 if (keyData
!= NULL
) {
536 if (isKnownKey
!= NULL
) {
539 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
543 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
544 UBool matched
= FALSE
;
545 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
546 matched
= isSpecialTypeCodepoints(type
);
548 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
549 matched
= isSpecialTypeReorderCode(type
);
552 if (isSpecialType
!= NULL
) {
553 *isSpecialType
= TRUE
;