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 **********************************************************************
11 #include "unicode/utypes.h"
12 #include "unicode/unistr.h"
13 #include "unicode/uobject.h"
24 #include "udataswp.h" /* for InvChar functions */
26 static UHashtable
* gLocExtKeyMap
= NULL
;
27 static icu::UInitOnce gLocExtKeyMapInitOnce
= U_INITONCE_INITIALIZER
;
29 // bit flags for special types
32 SPECIALTYPE_CODEPOINTS
= 1,
33 SPECIALTYPE_REORDER_CODE
= 2,
34 SPECIALTYPE_RG_KEY_VALUE
= 4
37 struct LocExtKeyData
: public icu::UMemory
{
40 icu::LocalUHashtablePointer typeMap
;
41 uint32_t specialTypes
;
44 struct LocExtType
: public icu::UMemory
{
49 static icu::MemoryPool
<icu::CharString
>* gKeyTypeStringPool
= NULL
;
50 static icu::MemoryPool
<LocExtKeyData
>* gLocExtKeyDataEntries
= NULL
;
51 static icu::MemoryPool
<LocExtType
>* gLocExtTypeEntries
= NULL
;
55 static UBool U_CALLCONV
56 uloc_key_type_cleanup(void) {
57 if (gLocExtKeyMap
!= NULL
) {
58 uhash_close(gLocExtKeyMap
);
62 delete gLocExtKeyDataEntries
;
63 gLocExtKeyDataEntries
= NULL
;
65 delete gLocExtTypeEntries
;
66 gLocExtTypeEntries
= NULL
;
68 delete gKeyTypeStringPool
;
69 gKeyTypeStringPool
= NULL
;
71 gLocExtKeyMapInitOnce
.reset();
78 static void U_CALLCONV
79 initFromResourceBundle(UErrorCode
& sts
) {
81 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE
, uloc_key_type_cleanup
);
83 gLocExtKeyMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
85 LocalUResourceBundlePointer
keyTypeDataRes(ures_openDirect(NULL
, "keyTypeData", &sts
));
86 LocalUResourceBundlePointer
keyMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "keyMap", NULL
, &sts
));
87 LocalUResourceBundlePointer
typeMapRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeMap", NULL
, &sts
));
93 UErrorCode tmpSts
= U_ZERO_ERROR
;
94 LocalUResourceBundlePointer
typeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "typeAlias", NULL
, &tmpSts
));
95 tmpSts
= U_ZERO_ERROR
;
96 LocalUResourceBundlePointer
bcpTypeAliasRes(ures_getByKey(keyTypeDataRes
.getAlias(), "bcpTypeAlias", NULL
, &tmpSts
));
98 // initialize pools storing dynamically allocated objects
99 gKeyTypeStringPool
= new icu::MemoryPool
<icu::CharString
>;
100 if (gKeyTypeStringPool
== NULL
) {
101 sts
= U_MEMORY_ALLOCATION_ERROR
;
104 gLocExtKeyDataEntries
= new icu::MemoryPool
<LocExtKeyData
>;
105 if (gLocExtKeyDataEntries
== NULL
) {
106 sts
= U_MEMORY_ALLOCATION_ERROR
;
109 gLocExtTypeEntries
= new icu::MemoryPool
<LocExtType
>;
110 if (gLocExtTypeEntries
== NULL
) {
111 sts
= U_MEMORY_ALLOCATION_ERROR
;
115 // iterate through keyMap resource
116 LocalUResourceBundlePointer keyMapEntry
;
118 while (ures_hasNext(keyMapRes
.getAlias())) {
119 keyMapEntry
.adoptInstead(ures_getNextResource(keyMapRes
.getAlias(), keyMapEntry
.orphan(), &sts
));
120 if (U_FAILURE(sts
)) {
123 const char* legacyKeyId
= ures_getKey(keyMapEntry
.getAlias());
124 UnicodeString uBcpKeyId
= ures_getUnicodeString(keyMapEntry
.getAlias(), &sts
);
125 if (U_FAILURE(sts
)) {
129 // empty value indicates that BCP key is same with the legacy key.
130 const char* bcpKeyId
= legacyKeyId
;
131 if (!uBcpKeyId
.isEmpty()) {
132 icu::CharString
* bcpKeyIdBuf
= gKeyTypeStringPool
->create();
133 if (bcpKeyIdBuf
== NULL
) {
134 sts
= U_MEMORY_ALLOCATION_ERROR
;
137 bcpKeyIdBuf
->appendInvariantChars(uBcpKeyId
, sts
);
138 if (U_FAILURE(sts
)) {
141 bcpKeyId
= bcpKeyIdBuf
->data();
144 UBool isTZ
= uprv_strcmp(legacyKeyId
, "timezone") == 0;
146 UHashtable
* typeDataMap
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, NULL
, &sts
);
147 if (U_FAILURE(sts
)) {
150 uint32_t specialTypes
= SPECIALTYPE_NONE
;
152 LocalUResourceBundlePointer typeAliasResByKey
;
153 LocalUResourceBundlePointer bcpTypeAliasResByKey
;
155 if (typeAliasRes
.isValid()) {
156 tmpSts
= U_ZERO_ERROR
;
157 typeAliasResByKey
.adoptInstead(ures_getByKey(typeAliasRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
158 if (U_FAILURE(tmpSts
)) {
159 typeAliasResByKey
.orphan();
162 if (bcpTypeAliasRes
.isValid()) {
163 tmpSts
= U_ZERO_ERROR
;
164 bcpTypeAliasResByKey
.adoptInstead(ures_getByKey(bcpTypeAliasRes
.getAlias(), bcpKeyId
, NULL
, &tmpSts
));
165 if (U_FAILURE(tmpSts
)) {
166 bcpTypeAliasResByKey
.orphan();
170 // look up type map for the key, and walk through the mapping data
171 tmpSts
= U_ZERO_ERROR
;
172 LocalUResourceBundlePointer
typeMapResByKey(ures_getByKey(typeMapRes
.getAlias(), legacyKeyId
, NULL
, &tmpSts
));
173 if (U_FAILURE(tmpSts
)) {
174 // type map for each key must exist
177 LocalUResourceBundlePointer typeMapEntry
;
179 while (ures_hasNext(typeMapResByKey
.getAlias())) {
180 typeMapEntry
.adoptInstead(ures_getNextResource(typeMapResByKey
.getAlias(), typeMapEntry
.orphan(), &sts
));
181 if (U_FAILURE(sts
)) {
184 const char* legacyTypeId
= ures_getKey(typeMapEntry
.getAlias());
187 if (uprv_strcmp(legacyTypeId
, "CODEPOINTS") == 0) {
188 specialTypes
|= SPECIALTYPE_CODEPOINTS
;
191 if (uprv_strcmp(legacyTypeId
, "REORDER_CODE") == 0) {
192 specialTypes
|= SPECIALTYPE_REORDER_CODE
;
195 if (uprv_strcmp(legacyTypeId
, "RG_KEY_VALUE") == 0) {
196 specialTypes
|= SPECIALTYPE_RG_KEY_VALUE
;
201 // a timezone key uses a colon instead of a slash in the resource.
202 // e.g. America:Los_Angeles
203 if (uprv_strchr(legacyTypeId
, ':') != NULL
) {
204 icu::CharString
* legacyTypeIdBuf
=
205 gKeyTypeStringPool
->create(legacyTypeId
, sts
);
206 if (legacyTypeIdBuf
== NULL
) {
207 sts
= U_MEMORY_ALLOCATION_ERROR
;
210 if (U_FAILURE(sts
)) {
214 legacyTypeIdBuf
->data(),
215 legacyTypeIdBuf
->data() + legacyTypeIdBuf
->length(),
217 legacyTypeId
= legacyTypeIdBuf
->data();
221 UnicodeString uBcpTypeId
= ures_getUnicodeString(typeMapEntry
.getAlias(), &sts
);
222 if (U_FAILURE(sts
)) {
226 // empty value indicates that BCP type is same with the legacy type.
227 const char* bcpTypeId
= legacyTypeId
;
228 if (!uBcpTypeId
.isEmpty()) {
229 icu::CharString
* bcpTypeIdBuf
= gKeyTypeStringPool
->create();
230 if (bcpTypeIdBuf
== NULL
) {
231 sts
= U_MEMORY_ALLOCATION_ERROR
;
234 bcpTypeIdBuf
->appendInvariantChars(uBcpTypeId
, sts
);
235 if (U_FAILURE(sts
)) {
238 bcpTypeId
= bcpTypeIdBuf
->data();
241 // Note: legacy type value should never be
242 // equivalent to bcp type value of a different
243 // type under the same key. So we use a single
245 LocExtType
* t
= gLocExtTypeEntries
->create();
247 sts
= U_MEMORY_ALLOCATION_ERROR
;
250 t
->bcpId
= bcpTypeId
;
251 t
->legacyId
= legacyTypeId
;
253 uhash_put(typeDataMap
, (void*)legacyTypeId
, t
, &sts
);
254 if (bcpTypeId
!= legacyTypeId
) {
255 // different type value
256 uhash_put(typeDataMap
, (void*)bcpTypeId
, t
, &sts
);
258 if (U_FAILURE(sts
)) {
262 // also put aliases in the map
263 if (typeAliasResByKey
.isValid()) {
264 LocalUResourceBundlePointer typeAliasDataEntry
;
266 ures_resetIterator(typeAliasResByKey
.getAlias());
267 while (ures_hasNext(typeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
269 typeAliasDataEntry
.adoptInstead(ures_getNextResource(typeAliasResByKey
.getAlias(), typeAliasDataEntry
.orphan(), &sts
));
270 const UChar
* to
= ures_getString(typeAliasDataEntry
.getAlias(), &toLen
, &sts
);
271 if (U_FAILURE(sts
)) {
274 // check if this is an alias of canoncal legacy type
275 if (uprv_compareInvWithUChar(NULL
, legacyTypeId
, -1, to
, toLen
) == 0) {
276 const char* from
= ures_getKey(typeAliasDataEntry
.getAlias());
278 // replace colon with slash if necessary
279 if (uprv_strchr(from
, ':') != NULL
) {
280 icu::CharString
* fromBuf
=
281 gKeyTypeStringPool
->create(from
, sts
);
282 if (fromBuf
== NULL
) {
283 sts
= U_MEMORY_ALLOCATION_ERROR
;
286 if (U_FAILURE(sts
)) {
291 fromBuf
->data() + fromBuf
->length(),
293 from
= fromBuf
->data();
296 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
299 if (U_FAILURE(sts
)) {
304 if (bcpTypeAliasResByKey
.isValid()) {
305 LocalUResourceBundlePointer bcpTypeAliasDataEntry
;
307 ures_resetIterator(bcpTypeAliasResByKey
.getAlias());
308 while (ures_hasNext(bcpTypeAliasResByKey
.getAlias()) && U_SUCCESS(sts
)) {
310 bcpTypeAliasDataEntry
.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey
.getAlias(), bcpTypeAliasDataEntry
.orphan(), &sts
));
311 const UChar
* to
= ures_getString(bcpTypeAliasDataEntry
.getAlias(), &toLen
, &sts
);
312 if (U_FAILURE(sts
)) {
315 // check if this is an alias of bcp type
316 if (uprv_compareInvWithUChar(NULL
, bcpTypeId
, -1, to
, toLen
) == 0) {
317 const char* from
= ures_getKey(bcpTypeAliasDataEntry
.getAlias());
318 uhash_put(typeDataMap
, (void*)from
, t
, &sts
);
321 if (U_FAILURE(sts
)) {
327 if (U_FAILURE(sts
)) {
331 LocExtKeyData
* keyData
= gLocExtKeyDataEntries
->create();
332 if (keyData
== NULL
) {
333 sts
= U_MEMORY_ALLOCATION_ERROR
;
336 keyData
->bcpId
= bcpKeyId
;
337 keyData
->legacyId
= legacyKeyId
;
338 keyData
->specialTypes
= specialTypes
;
339 keyData
->typeMap
.adoptInstead(typeDataMap
);
341 uhash_put(gLocExtKeyMap
, (void*)legacyKeyId
, keyData
, &sts
);
342 if (legacyKeyId
!= bcpKeyId
) {
343 // different key value
344 uhash_put(gLocExtKeyMap
, (void*)bcpKeyId
, keyData
, &sts
);
346 if (U_FAILURE(sts
)) {
354 UErrorCode sts
= U_ZERO_ERROR
;
355 umtx_initOnce(gLocExtKeyMapInitOnce
, &initFromResourceBundle
, sts
);
356 if (U_FAILURE(sts
)) {
363 isSpecialTypeCodepoints(const char* val
) {
364 int32_t subtagLen
= 0;
368 if (subtagLen
< 4 || subtagLen
> 6) {
372 } else if ((*p
>= '0' && *p
<= '9') ||
373 (*p
>= 'A' && *p
<= 'F') || // A-F/a-f are contiguous
374 (*p
>= 'a' && *p
<= 'f')) { // also in EBCDIC
381 return (subtagLen
>= 4 && subtagLen
<= 6);
385 isSpecialTypeReorderCode(const char* val
) {
386 int32_t subtagLen
= 0;
390 if (subtagLen
< 3 || subtagLen
> 8) {
394 } else if (uprv_isASCIILetter(*p
)) {
401 return (subtagLen
>=3 && subtagLen
<=8);
405 isSpecialTypeRgKeyValue(const char* val
) {
406 int32_t subtagLen
= 0;
409 if ( (subtagLen
< 2 && uprv_isASCIILetter(*p
)) ||
410 (subtagLen
>= 2 && (*p
== 'Z' || *p
== 'z')) ) {
417 return (subtagLen
== 6);
421 ulocimp_toBcpKey(const char* key
) {
426 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
427 if (keyData
!= NULL
) {
428 return keyData
->bcpId
;
434 ulocimp_toLegacyKey(const char* key
) {
439 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
440 if (keyData
!= NULL
) {
441 return keyData
->legacyId
;
447 ulocimp_toBcpType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
448 if (isKnownKey
!= NULL
) {
451 if (isSpecialType
!= NULL
) {
452 *isSpecialType
= FALSE
;
459 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
460 if (keyData
!= NULL
) {
461 if (isKnownKey
!= NULL
) {
464 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
.getAlias(), type
);
468 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
469 UBool matched
= FALSE
;
470 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
471 matched
= isSpecialTypeCodepoints(type
);
473 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
474 matched
= isSpecialTypeReorderCode(type
);
476 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
477 matched
= isSpecialTypeRgKeyValue(type
);
480 if (isSpecialType
!= NULL
) {
481 *isSpecialType
= TRUE
;
492 ulocimp_toLegacyType(const char* key
, const char* type
, UBool
* isKnownKey
, UBool
* isSpecialType
) {
493 if (isKnownKey
!= NULL
) {
496 if (isSpecialType
!= NULL
) {
497 *isSpecialType
= FALSE
;
504 LocExtKeyData
* keyData
= (LocExtKeyData
*)uhash_get(gLocExtKeyMap
, key
);
505 if (keyData
!= NULL
) {
506 if (isKnownKey
!= NULL
) {
509 LocExtType
* t
= (LocExtType
*)uhash_get(keyData
->typeMap
.getAlias(), type
);
513 if (keyData
->specialTypes
!= SPECIALTYPE_NONE
) {
514 UBool matched
= FALSE
;
515 if (keyData
->specialTypes
& SPECIALTYPE_CODEPOINTS
) {
516 matched
= isSpecialTypeCodepoints(type
);
518 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_REORDER_CODE
) {
519 matched
= isSpecialTypeReorderCode(type
);
521 if (!matched
&& keyData
->specialTypes
& SPECIALTYPE_RG_KEY_VALUE
) {
522 matched
= isSpecialTypeRgKeyValue(type
);
525 if (isSpecialType
!= NULL
) {
526 *isSpecialType
= TRUE
;