1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 **********************************************************************
5 * Copyright (C) 2014-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
9 #include "unicode/utypes.h"
18 #include "udataswp.h" /* for InvChar functions */
20 static UHashtable
* gLocExtKeyMap
= NULL
;
21 static icu::UInitOnce gLocExtKeyMapInitOnce
= U_INITONCE_INITIALIZER
;
22 static icu::UVector
* gKeyTypeStringPool
= NULL
;
23 static icu::UVector
* gLocExtKeyDataEntries
= NULL
;
24 static icu::UVector
* gLocExtTypeEntries
= NULL
;
26 // bit flags for special types
29 SPECIALTYPE_CODEPOINTS
= 1,
30 SPECIALTYPE_REORDER_CODE
= 2,
31 SPECIALTYPE_RG_KEY_VALUE
= 4
34 typedef struct LocExtKeyData
{
38 uint32_t specialTypes
;
41 typedef struct LocExtType
{
48 static UBool U_CALLCONV
49 uloc_key_type_cleanup(void) {
50 if (gLocExtKeyMap
!= NULL
) {
51 uhash_close(gLocExtKeyMap
);
55 delete gLocExtKeyDataEntries
;
56 gLocExtKeyDataEntries
= NULL
;
58 delete gLocExtTypeEntries
;
59 gLocExtTypeEntries
= NULL
;
61 delete gKeyTypeStringPool
;
62 gKeyTypeStringPool
= NULL
;
64 gLocExtKeyMapInitOnce
.reset();
68 static void U_CALLCONV
69 uloc_deleteKeyTypeStringPoolEntry(void* obj
) {
73 static void U_CALLCONV
74 uloc_deleteKeyDataEntry(void* obj
) {
75 LocExtKeyData
* keyData
= (LocExtKeyData
*)obj
;
76 if (keyData
->typeMap
!= NULL
) {
77 uhash_close(keyData
->typeMap
);
82 static void U_CALLCONV
83 uloc_deleteTypeEntry(void* obj
) {
90 static void U_CALLCONV
91 initFromResourceBundle(UErrorCode
& sts
) {
93 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE
, uloc_key_type_cleanup
);
95 gLocExtKeyMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
97 LocalUResourceBundlePointer
keyTypeDataRes(ures_openDirect(NULL
, "keyTypeData", &sts
));
98 LocalUResourceBundlePointer
keyMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "keyMap", NULL
, &sts
));
99 LocalUResourceBundlePointer
typeMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeMap", NULL
, &sts
));
101 if (U_FAILURE(sts
)) {
105 UErrorCode tmpSts
= U_ZERO_ERROR
;
106 LocalUResourceBundlePointer
typeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeAlias", NULL
, &tmpSts
));
107 tmpSts
= U_ZERO_ERROR
;
108 LocalUResourceBundlePointer
bcpTypeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "bcpTypeAlias", NULL
, &tmpSts
));
110 // initialize vectors storing dynamically allocated objects
111 gKeyTypeStringPool
= new UVector(uloc_deleteKeyTypeStringPoolEntry
, NULL
, sts
);
112 if (gKeyTypeStringPool
== NULL
) {
113 if (U_SUCCESS(sts
)) {
114 sts
= U_MEMORY_ALLOCATION_ERROR
;
117 if (U_FAILURE(sts
)) {
120 gLocExtKeyDataEntries
= new UVector(uloc_deleteKeyDataEntry
, NULL
, sts
);
121 if (gLocExtKeyDataEntries
== NULL
) {
122 if (U_SUCCESS(sts
)) {
123 sts
= U_MEMORY_ALLOCATION_ERROR
;
126 if (U_FAILURE(sts
)) {
129 gLocExtTypeEntries
= new UVector(uloc_deleteTypeEntry
, NULL
, sts
);
130 if (gLocExtTypeEntries
== NULL
) {
131 if (U_SUCCESS(sts
)) {
132 sts
= U_MEMORY_ALLOCATION_ERROR
;
135 if (U_FAILURE(sts
)) {
139 // iterate through keyMap resource
140 LocalUResourceBundlePointer keyMapEntry
;
142 while (ures_hasNext(keyMapRes
.getAlias())) {
143 keyMapEntry
.adoptInstead(ures_getNextResource(keyMapRes
.getAlias(), keyMapEntry
.orphan(), &sts
));
144 if (U_FAILURE(sts
)) {
147 const char* legacyKeyId
= ures_getKey(keyMapEntry
.getAlias());
148 int32_t bcpKeyIdLen
= 0;
149 const UChar
* uBcpKeyId
= ures_getString(keyMapEntry
.getAlias(), &bcpKeyIdLen
, &sts
);
150 if (U_FAILURE(sts
)) {
154 // empty value indicates that BCP key is same with the legacy key.
155 const char* bcpKeyId
= legacyKeyId
;
156 if (bcpKeyIdLen
> 0) {
157 char* bcpKeyIdBuf
= (char*)uprv_malloc(bcpKeyIdLen
+ 1);
158 if (bcpKeyIdBuf
== NULL
) {
159 sts
= U_MEMORY_ALLOCATION_ERROR
;
162 u_UCharsToChars(uBcpKeyId
, bcpKeyIdBuf
, bcpKeyIdLen
);
163 bcpKeyIdBuf
[bcpKeyIdLen
] = 0;
164 gKeyTypeStringPool
->addElement(bcpKeyIdBuf
, sts
);
165 if (U_FAILURE(sts
)) {
168 bcpKeyId
= bcpKeyIdBuf
;
171 UBool isTZ
= uprv_strcmp(legacyKeyId
, "timezone") == 0;
173 UHashtable
* typeDataMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
174 if (U_FAILURE(sts
)) {
177 uint32_t specialTypes
= SPECIALTYPE_NONE
;
179 LocalUResourceBundlePointer typeAliasResByKey
;
180 LocalUResourceBundlePointer bcpTypeAliasResByKey
;
182 if (typeAliasRes
.isValid()) {
183 tmpSts
= U_ZERO_ERROR
;
184 typeAliasResByKey
.adoptInstead(ures_getByKey(typeAliasRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
185 if (U_FAILURE(tmpSts
)) {
186 typeAliasResByKey
.orphan();
189 if (bcpTypeAliasRes
.isValid()) {
190 tmpSts
= U_ZERO_ERROR
;
191 bcpTypeAliasResByKey
.adoptInstead(ures_getByKey(bcpTypeAliasRes
.getAlias(), bcpKeyId
, NULL
, &tmpSts
));
192 if (U_FAILURE(tmpSts
)) {
193 bcpTypeAliasResByKey
.orphan();
197 // look up type map for the key, and walk through the mapping data
198 tmpSts
= U_ZERO_ERROR
;
199 LocalUResourceBundlePointer
typeMapResByKey(ures_getByKey(typeMapRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
200 if (U_FAILURE(tmpSts
)) {
201 // type map for each key must exist
204 LocalUResourceBundlePointer typeMapEntry
;
206 while (ures_hasNext(typeMapResByKey
.getAlias())) {
207 typeMapEntry
.adoptInstead(ures_getNextResource(typeMapResByKey
.getAlias(), typeMapEntry
.orphan(), &sts
));
208 if (U_FAILURE(sts
)) {
211 const char* legacyTypeId
= ures_getKey(typeMapEntry
.getAlias());
214 if (uprv_strcmp(legacyTypeId
, "CODEPOINTS") == 0) {
215 specialTypes
|= SPECIALTYPE_CODEPOINTS
;
218 if (uprv_strcmp(legacyTypeId
, "REORDER_CODE") == 0) {
219 specialTypes
|= SPECIALTYPE_REORDER_CODE
;
222 if (uprv_strcmp(legacyTypeId
, "RG_KEY_VALUE") == 0) {
223 specialTypes
|= SPECIALTYPE_RG_KEY_VALUE
;
228 // a timezone key uses a colon instead of a slash in the resource.
229 // e.g. America:Los_Angeles
230 if (uprv_strchr(legacyTypeId
, ':') != NULL
) {
231 int32_t legacyTypeIdLen
= uprv_strlen(legacyTypeId
);
232 char* legacyTypeIdBuf
= (char*)uprv_malloc(legacyTypeIdLen
+ 1);
233 if (legacyTypeIdBuf
== NULL
) {
234 sts
= U_MEMORY_ALLOCATION_ERROR
;
237 const char* p
= legacyTypeId
;
238 char* q
= legacyTypeIdBuf
;
249 gKeyTypeStringPool
->addElement(legacyTypeIdBuf
, sts
);
250 if (U_FAILURE(sts
)) {
253 legacyTypeId
= legacyTypeIdBuf
;
257 int32_t bcpTypeIdLen
= 0;
258 const UChar
* uBcpTypeId
= ures_getString(typeMapEntry
.getAlias(), &bcpTypeIdLen
, &sts
);
259 if (U_FAILURE(sts
)) {
263 // empty value indicates that BCP type is same with the legacy type.
264 const char* bcpTypeId
= legacyTypeId
;
265 if (bcpTypeIdLen
> 0) {
266 char* bcpTypeIdBuf
= (char*)uprv_malloc(bcpTypeIdLen
+ 1);
267 if (bcpTypeIdBuf
== NULL
) {
268 sts
= U_MEMORY_ALLOCATION_ERROR
;
271 u_UCharsToChars(uBcpTypeId
, bcpTypeIdBuf
, bcpTypeIdLen
);
272 bcpTypeIdBuf
[bcpTypeIdLen
] = 0;
273 gKeyTypeStringPool
->addElement(bcpTypeIdBuf
, sts
);
274 if (U_FAILURE(sts
)) {
277 bcpTypeId
= bcpTypeIdBuf
;
280 // Note: legacy type value should never be
281 // equivalent to bcp type value of a different
282 // type under the same key. So we use a single
284 LocExtType
* t
= (LocExtType
*)uprv_malloc(sizeof(LocExtType
));
286 sts
= U_MEMORY_ALLOCATION_ERROR
;
289 t
->bcpId
= bcpTypeId
;
290 t
->legacyId
= legacyTypeId
;
291 gLocExtTypeEntries
->addElement((void*)t
, sts
);
292 if (U_FAILURE(sts
)) {
296 uhash_put(typeDataMap
, (void*)legacyTypeId
, t
, &sts
);
297 if (bcpTypeId
!= legacyTypeId
) {
298 // different type value
299 uhash_put(typeDataMap
, (void*)bcpTypeId
, t
, &sts
);
301 if (U_FAILURE(sts
)) {
305 // also put aliases in the map
306 if (typeAliasResByKey
.isValid()) {
307 LocalUResourceBundlePointer typeAliasDataEntry
;
309 ures_resetIterator(typeAliasResByKey
.getAlias());
310 while (ures_hasNext(typeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
312 typeAliasDataEntry
.adoptInstead(ures_getNextResource(typeAliasResByKey
.getAlias(), typeAliasDataEntry
.orphan(), &sts
));
313 const UChar
* to
= ures_getString(typeAliasDataEntry
.getAlias(), &toLen
, &sts
);
314 if (U_FAILURE(sts
)) {
317 // check if this is an alias of canoncal legacy type
318 if (uprv_compareInvWithUChar(NULL
, legacyTypeId
, -1, to
, toLen
) == 0) {
319 const char* from
= ures_getKey(typeAliasDataEntry
.getAlias());
321 // replace colon with slash if necessary
322 if (uprv_strchr(from
, ':') != NULL
) {
323 int32_t fromLen
= uprv_strlen(from
);
324 char* fromBuf
= (char*)uprv_malloc(fromLen
+ 1);
325 if (fromBuf
== NULL
) {
326 sts
= U_MEMORY_ALLOCATION_ERROR
;
329 const char* p
= from
;
341 gKeyTypeStringPool
->addElement(fromBuf
, sts
);
342 if (U_FAILURE(sts
)) {
348 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
351 if (U_FAILURE(sts
)) {
356 if (bcpTypeAliasResByKey
.isValid()) {
357 LocalUResourceBundlePointer bcpTypeAliasDataEntry
;
359 ures_resetIterator(bcpTypeAliasResByKey
.getAlias());
360 while (ures_hasNext(bcpTypeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
362 bcpTypeAliasDataEntry
.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey
.getAlias(), bcpTypeAliasDataEntry
.orphan(), &sts
));
363 const UChar
* to
= ures_getString(bcpTypeAliasDataEntry
.getAlias(), &toLen
, &sts
);
364 if (U_FAILURE(sts
)) {
367 // check if this is an alias of bcp type
368 if (uprv_compareInvWithUChar(NULL
, bcpTypeId
, -1, to
, toLen
) == 0) {
369 const char* from
= ures_getKey(bcpTypeAliasDataEntry
.getAlias());
370 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
373 if (U_FAILURE(sts
)) {
379 if (U_FAILURE(sts
)) {
383 LocExtKeyData
* keyData
= (LocExtKeyData
*)uprv_malloc(sizeof(LocExtKeyData
));
384 if (keyData
== NULL
) {
385 sts
= U_MEMORY_ALLOCATION_ERROR
;
388 keyData
->bcpId
= bcpKeyId
;
389 keyData
->legacyId
= legacyKeyId
;
390 keyData
->specialTypes
= specialTypes
;
391 keyData
->typeMap
= typeDataMap
;
393 gLocExtKeyDataEntries
->addElement((void*)keyData
, sts
);
394 if (U_FAILURE(sts
)) {
398 uhash_put(gLocExtKeyMap
, (void*)legacyKeyId
, keyData
, &sts
);
399 if (legacyKeyId
!= bcpKeyId
) {
400 // different key value
401 uhash_put(gLocExtKeyMap
, (void*)bcpKeyId
, keyData
, &sts
);
403 if (U_FAILURE(sts
)) {
411 UErrorCode sts
= U_ZERO_ERROR
;
412 umtx_initOnce(gLocExtKeyMapInitOnce
, &initFromResourceBundle
, sts
);
413 if (U_FAILURE(sts
)) {
420 isSpecialTypeCodepoints(const char* val
) {
421 int32_t subtagLen
= 0;
425 if (subtagLen
< 4 || subtagLen
> 6) {
429 } else if ((*p
>= '0' && *p
<= '9') ||
430 (*p
>= 'A' && *p
<= 'F') || // A-F/a-f are contiguous
431 (*p
>= 'a' && *p
<= 'f')) { // also in EBCDIC
438 return (subtagLen
>= 4 && subtagLen
<= 6);
442 isSpecialTypeReorderCode(const char* val
) {
443 int32_t subtagLen
= 0;
447 if (subtagLen
< 3 || subtagLen
> 8) {
451 } else if (uprv_isASCIILetter(*p
)) {
458 return (subtagLen
>=3 && subtagLen
<=8);
462 isSpecialTypeRgKeyValue(const char* val
) {
463 int32_t subtagLen
= 0;
466 if ( (subtagLen
< 2 && uprv_isASCIILetter(*p
)) ||
467 (subtagLen
>= 2 && (*p
== 'Z' || *p
== 'z')) ) {
474 return (subtagLen
== 6);
479 ulocimp_toBcpKey(const char* key
) {
484 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
485 if (keyData
!= NULL
) {
486 return keyData
->bcpId
;
492 ulocimp_toLegacyKey(const char* key
) {
497 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
498 if (keyData
!= NULL
) {
499 return keyData
->legacyId
;
505 ulocimp_toBcpType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
506 if (isKnownKey
!= NULL
) {
509 if (isSpecialType
!= NULL
) {
510 *isSpecialType
= FALSE
;
517 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
518 if (keyData
!= NULL
) {
519 if (isKnownKey
!= NULL
) {
522 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
526 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
527 UBool matched
= FALSE
;
528 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
529 matched
= isSpecialTypeCodepoints(type
);
531 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
532 matched
= isSpecialTypeReorderCode(type
);
534 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
535 matched
= isSpecialTypeRgKeyValue(type
);
538 if (isSpecialType
!= NULL
) {
539 *isSpecialType
= TRUE
;
550 ulocimp_toLegacyType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
551 if (isKnownKey
!= NULL
) {
554 if (isSpecialType
!= NULL
) {
555 *isSpecialType
= FALSE
;
562 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
563 if (keyData
!= NULL
) {
564 if (isKnownKey
!= NULL
) {
567 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
, type
);
571 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
572 UBool matched
= FALSE
;
573 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
574 matched
= isSpecialTypeCodepoints(type
);
576 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
577 matched
= isSpecialTypeReorderCode(type
);
579 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
580 matched
= isSpecialTypeRgKeyValue(type
);
583 if (isSpecialType
!= NULL
) {
584 *isSpecialType
= TRUE
;