2 **********************************************************************
3 * Copyright (C) 2014-2016, 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,
28 SPECIALTYPE_RG_KEY_VALUE
= 4
31 typedef struct LocExtKeyData
{
35 uint32_t specialTypes
;
38 typedef struct LocExtType
{
45 static UBool U_CALLCONV
46 uloc_key_type_cleanup(void) {
47 if (gLocExtKeyMap
!= NULL
) {
48 uhash_close(gLocExtKeyMap
);
52 delete gLocExtKeyDataEntries
;
53 gLocExtKeyDataEntries
= NULL
;
55 delete gLocExtTypeEntries
;
56 gLocExtTypeEntries
= NULL
;
58 delete gKeyTypeStringPool
;
59 gKeyTypeStringPool
= NULL
;
61 gLocExtKeyMapInitOnce
.reset();
65 static void U_CALLCONV
66 uloc_deleteKeyTypeStringPoolEntry(void* obj
) {
70 static void U_CALLCONV
71 uloc_deleteKeyDataEntry(void* obj
) {
72 LocExtKeyData
* keyData
= (LocExtKeyData
*)obj
;
73 if (keyData
->typeMap
!= NULL
) {
74 uhash_close(keyData
->typeMap
);
79 static void U_CALLCONV
80 uloc_deleteTypeEntry(void* obj
) {
87 static void U_CALLCONV
88 initFromResourceBundle(UErrorCode
& sts
) {
90 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE
, uloc_key_type_cleanup
);
92 gLocExtKeyMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
94 LocalUResourceBundlePointer
keyTypeDataRes(ures_openDirect(NULL
, "keyTypeData", &sts
));
95 LocalUResourceBundlePointer
keyMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "keyMap", NULL
, &sts
));
96 LocalUResourceBundlePointer
typeMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeMap", NULL
, &sts
));
102 UErrorCode tmpSts
= U_ZERO_ERROR
;
103 LocalUResourceBundlePointer
typeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeAlias", NULL
, &tmpSts
));
104 tmpSts
= U_ZERO_ERROR
;
105 LocalUResourceBundlePointer
bcpTypeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "bcpTypeAlias", NULL
, &tmpSts
));
107 // initialize vectors storing dynamically allocated objects
108 gKeyTypeStringPool
= new UVector(uloc_deleteKeyTypeStringPoolEntry
, NULL
, sts
);
109 if (gKeyTypeStringPool
== NULL
) {
110 if (U_SUCCESS(sts
)) {
111 sts
= U_MEMORY_ALLOCATION_ERROR
;
114 if (U_FAILURE(sts
)) {
117 gLocExtKeyDataEntries
= new UVector(uloc_deleteKeyDataEntry
, NULL
, sts
);
118 if (gLocExtKeyDataEntries
== NULL
) {
119 if (U_SUCCESS(sts
)) {
120 sts
= U_MEMORY_ALLOCATION_ERROR
;
123 if (U_FAILURE(sts
)) {
126 gLocExtTypeEntries
= new UVector(uloc_deleteTypeEntry
, NULL
, sts
);
127 if (gLocExtTypeEntries
== NULL
) {
128 if (U_SUCCESS(sts
)) {
129 sts
= U_MEMORY_ALLOCATION_ERROR
;
132 if (U_FAILURE(sts
)) {
136 // iterate through keyMap resource
137 LocalUResourceBundlePointer keyMapEntry
;
139 while (ures_hasNext(keyMapRes
.getAlias())) {
140 keyMapEntry
.adoptInstead(ures_getNextResource(keyMapRes
.getAlias(), keyMapEntry
.orphan(), &sts
));
141 if (U_FAILURE(sts
)) {
144 const char* legacyKeyId
= ures_getKey(keyMapEntry
.getAlias());
145 int32_t bcpKeyIdLen
= 0;
146 const UChar
* uBcpKeyId
= ures_getString(keyMapEntry
.getAlias(), &bcpKeyIdLen
, &sts
);
147 if (U_FAILURE(sts
)) {
151 // empty value indicates that BCP key is same with the legacy key.
152 const char* bcpKeyId
= legacyKeyId
;
153 if (bcpKeyIdLen
> 0) {
154 char* bcpKeyIdBuf
= (char*)uprv_malloc(bcpKeyIdLen
+ 1);
155 if (bcpKeyIdBuf
== NULL
) {
156 sts
= U_MEMORY_ALLOCATION_ERROR
;
159 u_UCharsToChars(uBcpKeyId
, bcpKeyIdBuf
, bcpKeyIdLen
);
160 bcpKeyIdBuf
[bcpKeyIdLen
] = 0;
161 gKeyTypeStringPool
->addElement(bcpKeyIdBuf
, sts
);
162 if (U_FAILURE(sts
)) {
165 bcpKeyId
= bcpKeyIdBuf
;
168 UBool isTZ
= uprv_strcmp(legacyKeyId
, "timezone") == 0;
170 UHashtable
* typeDataMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
171 if (U_FAILURE(sts
)) {
174 uint32_t specialTypes
= SPECIALTYPE_NONE
;
176 LocalUResourceBundlePointer typeAliasResByKey
;
177 LocalUResourceBundlePointer bcpTypeAliasResByKey
;
179 if (typeAliasRes
.isValid()) {
180 tmpSts
= U_ZERO_ERROR
;
181 typeAliasResByKey
.adoptInstead(ures_getByKey(typeAliasRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
182 if (U_FAILURE(tmpSts
)) {
183 typeAliasResByKey
.orphan();
186 if (bcpTypeAliasRes
.isValid()) {
187 tmpSts
= U_ZERO_ERROR
;
188 bcpTypeAliasResByKey
.adoptInstead(ures_getByKey(bcpTypeAliasRes
.getAlias(), bcpKeyId
, NULL
, &tmpSts
));
189 if (U_FAILURE(tmpSts
)) {
190 bcpTypeAliasResByKey
.orphan();
194 // look up type map for the key, and walk through the mapping data
195 tmpSts
= U_ZERO_ERROR
;
196 LocalUResourceBundlePointer
typeMapResByKey(ures_getByKey(typeMapRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
197 if (U_FAILURE(tmpSts
)) {
198 // type map for each key must exist
201 LocalUResourceBundlePointer typeMapEntry
;
203 while (ures_hasNext(typeMapResByKey
.getAlias())) {
204 typeMapEntry
.adoptInstead(ures_getNextResource(typeMapResByKey
.getAlias(), typeMapEntry
.orphan(), &sts
));
205 if (U_FAILURE(sts
)) {
208 const char* legacyTypeId
= ures_getKey(typeMapEntry
.getAlias());
211 if (uprv_strcmp(legacyTypeId
, "CODEPOINTS") == 0) {
212 specialTypes
|= SPECIALTYPE_CODEPOINTS
;
215 if (uprv_strcmp(legacyTypeId
, "REORDER_CODE") == 0) {
216 specialTypes
|= SPECIALTYPE_REORDER_CODE
;
219 if (uprv_strcmp(legacyTypeId
, "RG_KEY_VALUE") == 0) {
220 specialTypes
|= SPECIALTYPE_RG_KEY_VALUE
;
225 // a timezone key uses a colon instead of a slash in the resource.
226 // e.g. America:Los_Angeles
227 if (uprv_strchr(legacyTypeId
, ':') != NULL
) {
228 int32_t legacyTypeIdLen
= uprv_strlen(legacyTypeId
);
229 char* legacyTypeIdBuf
= (char*)uprv_malloc(legacyTypeIdLen
+ 1);
230 if (legacyTypeIdBuf
== NULL
) {
231 sts
= U_MEMORY_ALLOCATION_ERROR
;
234 const char* p
= legacyTypeId
;
235 char* q
= legacyTypeIdBuf
;
246 gKeyTypeStringPool
->addElement(legacyTypeIdBuf
, sts
);
247 if (U_FAILURE(sts
)) {
250 legacyTypeId
= legacyTypeIdBuf
;
254 int32_t bcpTypeIdLen
= 0;
255 const UChar
* uBcpTypeId
= ures_getString(typeMapEntry
.getAlias(), &bcpTypeIdLen
, &sts
);
256 if (U_FAILURE(sts
)) {
260 // empty value indicates that BCP type is same with the legacy type.
261 const char* bcpTypeId
= legacyTypeId
;
262 if (bcpTypeIdLen
> 0) {
263 char* bcpTypeIdBuf
= (char*)uprv_malloc(bcpTypeIdLen
+ 1);
264 if (bcpTypeIdBuf
== NULL
) {
265 sts
= U_MEMORY_ALLOCATION_ERROR
;
268 u_UCharsToChars(uBcpTypeId
, bcpTypeIdBuf
, bcpTypeIdLen
);
269 bcpTypeIdBuf
[bcpTypeIdLen
] = 0;
270 gKeyTypeStringPool
->addElement(bcpTypeIdBuf
, sts
);
271 if (U_FAILURE(sts
)) {
274 bcpTypeId
= bcpTypeIdBuf
;
277 // Note: legacy type value should never be
278 // equivalent to bcp type value of a different
279 // type under the same key. So we use a single
281 LocExtType
* t
= (LocExtType
*)uprv_malloc(sizeof(LocExtType
));
283 sts
= U_MEMORY_ALLOCATION_ERROR
;
286 t
->bcpId
= bcpTypeId
;
287 t
->legacyId
= legacyTypeId
;
288 gLocExtTypeEntries
->addElement((void*)t
, sts
);
289 if (U_FAILURE(sts
)) {
293 uhash_put(typeDataMap
, (void*)legacyTypeId
, t
, &sts
);
294 if (bcpTypeId
!= legacyTypeId
) {
295 // different type value
296 uhash_put(typeDataMap
, (void*)bcpTypeId
, t
, &sts
);
298 if (U_FAILURE(sts
)) {
302 // also put aliases in the map
303 if (typeAliasResByKey
.isValid()) {
304 LocalUResourceBundlePointer typeAliasDataEntry
;
306 ures_resetIterator(typeAliasResByKey
.getAlias());
307 while (ures_hasNext(typeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
309 typeAliasDataEntry
.adoptInstead(ures_getNextResource(typeAliasResByKey
.getAlias(), typeAliasDataEntry
.orphan(), &sts
));
310 const UChar
* to
= ures_getString(typeAliasDataEntry
.getAlias(), &toLen
, &sts
);
311 if (U_FAILURE(sts
)) {
314 // check if this is an alias of canoncal legacy type
315 if (uprv_compareInvAscii(NULL
, legacyTypeId
, -1, to
, toLen
) == 0) {
316 const char* from
= ures_getKey(typeAliasDataEntry
.getAlias());
318 // replace colon with slash if necessary
319 if (uprv_strchr(from
, ':') != NULL
) {
320 int32_t fromLen
= uprv_strlen(from
);
321 char* fromBuf
= (char*)uprv_malloc(fromLen
+ 1);
322 if (fromBuf
== NULL
) {
323 sts
= U_MEMORY_ALLOCATION_ERROR
;
326 const char* p
= from
;
338 gKeyTypeStringPool
->addElement(fromBuf
, sts
);
339 if (U_FAILURE(sts
)) {
345 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
348 if (U_FAILURE(sts
)) {
353 if (bcpTypeAliasResByKey
.isValid()) {
354 LocalUResourceBundlePointer bcpTypeAliasDataEntry
;
356 ures_resetIterator(bcpTypeAliasResByKey
.getAlias());
357 while (ures_hasNext(bcpTypeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
359 bcpTypeAliasDataEntry
.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey
.getAlias(), bcpTypeAliasDataEntry
.orphan(), &sts
));
360 const UChar
* to
= ures_getString(bcpTypeAliasDataEntry
.getAlias(), &toLen
, &sts
);
361 if (U_FAILURE(sts
)) {
364 // check if this is an alias of bcp type
365 if (uprv_compareInvAscii(NULL
, bcpTypeId
, -1, to
, toLen
) == 0) {
366 const char* from
= ures_getKey(bcpTypeAliasDataEntry
.getAlias());
367 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
370 if (U_FAILURE(sts
)) {
376 if (U_FAILURE(sts
)) {
380 LocExtKeyData
* keyData
= (LocExtKeyData
*)uprv_malloc(sizeof(LocExtKeyData
));
381 if (keyData
== NULL
) {
382 sts
= U_MEMORY_ALLOCATION_ERROR
;
385 keyData
->bcpId
= bcpKeyId
;
386 keyData
->legacyId
= legacyKeyId
;
387 keyData
->specialTypes
= specialTypes
;
388 keyData
->typeMap
= typeDataMap
;
390 gLocExtKeyDataEntries
->addElement((void*)keyData
, sts
);
391 if (U_FAILURE(sts
)) {
395 uhash_put(gLocExtKeyMap
, (void*)legacyKeyId
, keyData
, &sts
);
396 if (legacyKeyId
!= bcpKeyId
) {
397 // different key value
398 uhash_put(gLocExtKeyMap
, (void*)bcpKeyId
, keyData
, &sts
);
400 if (U_FAILURE(sts
)) {
408 UErrorCode sts
= U_ZERO_ERROR
;
409 umtx_initOnce(gLocExtKeyMapInitOnce
, &initFromResourceBundle
, sts
);
410 if (U_FAILURE(sts
)) {
417 isSpecialTypeCodepoints(const char* val
) {
418 int32_t subtagLen
= 0;
422 if (subtagLen
< 4 || subtagLen
> 6) {
426 } else if ((*p
>= '0' && *p
<= '9') ||
427 (*p
>= 'A' && *p
<= 'F') || // A-F/a-f are contiguous
428 (*p
>= 'a' && *p
<= 'f')) { // also in EBCDIC
435 return (subtagLen
>= 4 && subtagLen
<= 6);
439 isSpecialTypeReorderCode(const char* val
) {
440 int32_t subtagLen
= 0;
444 if (subtagLen
< 3 || subtagLen
> 8) {
448 } else if (uprv_isASCIILetter(*p
)) {
455 return (subtagLen
>=3 && subtagLen
<=8);
459 isSpecialTypeRgKeyValue(const char* val
) {
460 int32_t subtagLen
= 0;
463 if ( (subtagLen
< 2 && uprv_isASCIILetter(*p
)) ||
464 (subtagLen
>= 2 && (*p
== 'Z' || *p
== 'z')) ) {
471 return (subtagLen
== 6);
476 ulocimp_toBcpKey(const char* key
) {
481 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
482 if (keyData
!= NULL
) {
483 return keyData
->bcpId
;
489 ulocimp_toLegacyKey(const char* key
) {
494 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
495 if (keyData
!= NULL
) {
496 return keyData
->legacyId
;
502 ulocimp_toBcpType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
503 if (isKnownKey
!= NULL
) {
506 if (isSpecialType
!= NULL
) {
507 *isSpecialType
= FALSE
;
514 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
515 if (keyData
!= NULL
) {
516 if (isKnownKey
!= NULL
) {
519 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
523 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
524 UBool matched
= FALSE
;
525 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
526 matched
= isSpecialTypeCodepoints(type
);
528 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
529 matched
= isSpecialTypeReorderCode(type
);
531 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
532 matched
= isSpecialTypeRgKeyValue(type
);
535 if (isSpecialType
!= NULL
) {
536 *isSpecialType
= TRUE
;
547 ulocimp_toLegacyType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
548 if (isKnownKey
!= NULL
) {
551 if (isSpecialType
!= NULL
) {
552 *isSpecialType
= FALSE
;
559 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
560 if (keyData
!= NULL
) {
561 if (isKnownKey
!= NULL
) {
564 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
568 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
569 UBool matched
= FALSE
;
570 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
571 matched
= isSpecialTypeCodepoints(type
);
573 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
574 matched
= isSpecialTypeReorderCode(type
);
576 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
577 matched
= isSpecialTypeRgKeyValue(type
);
580 if (isSpecialType
!= NULL
) {
581 *isSpecialType
= TRUE
;