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
;