Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2009, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include "zonemeta.h"
     13 
     14 #include "unicode/timezone.h"
     15 #include "unicode/ustring.h"
     16 #include "unicode/putil.h"
     17 
     18 #include "umutex.h"
     19 #include "uvector.h"
     20 #include "cmemory.h"
     21 #include "gregoimp.h"
     22 #include "cstring.h"
     23 #include "ucln_in.h"
     24 
     25 // Metazone mapping tables
     26 static UMTX gZoneMetaLock = NULL;
     27 static UHashtable *gCanonicalMap = NULL;
     28 static UHashtable *gOlsonToMeta = NULL;
     29 static UHashtable *gMetaToOlson = NULL;
     30 static UBool gCanonicalMapInitialized = FALSE;
     31 static UBool gOlsonToMetaInitialized = FALSE;
     32 static UBool gMetaToOlsonInitialized = FALSE;
     33 static UChar **gUStringTable = NULL;
     34 static int32_t gUStringCount = 0;
     35 static int32_t gUStringAlloc = 0;
     36 
     37 // Currently (ICU 4.1.3+), gUStringTable only contains strings allocated in the section of
     38 // createCanonicalMap that iterates over the enumerator created with TimeZone::createEnumeration.
     39 // And currently, that allocates a total of 22 strings. So USTRING_ALLOC_START is defined to
     40 // be adequate for that set, and USTRING_ALLOC_INCR is a reasonable expansion increment. In
     41 // future versions of ICU, these numbers may need adjusting to avoid excessive reallocs, or to
     42 // avoid allocating unused memory (but in any case the effects are small).
     43 #define USTRING_ALLOC_START 24
     44 #define USTRING_ALLOC_INCR 12
     45 
     46 U_CDECL_BEGIN
     47 
     48 // We have switched CanonicalMap to use const UChar* strings for the key and for the id field of
     49 // CanonicalMapEntry; that is because for the most part these now point into UChar strings in the
     50 // shared data file, in order to reduce process-specific dynamically-allocated memory. Consequently,
     51 // there is no longer a deleter for the key field, and the deleter for CanonicalMapEntry
     52 // no longer frees the id field. However, for the few strings that are obtained from the
     53 // TimeZone::createEnumeration() enumerator or from TimeZone::dereferOlsonLink instead of the
     54 // data file, we do need to allocate copies. In order to ensure that these strings are freed by
     55 // zoneMeta_cleanup(), we need to create a little memory manager for them; this is in the form of
     56 // a table that tracks the strings allocated for this purpose. The following three functions
     57 // (along with the gUStringXxxxx statics) are used to allocate and free such strings.
     58 
     59 // The following allocs space for a UChar* string of the specified length, puts a pointer to the string
     60 // in gUStringTable, and returns either a pointer to the allocated string space, or NULL for failure.
     61 static UChar * allocUStringInTable(int32_t uStringLen) {
     62     UChar * uStringSpace = NULL;
     63     // initialize the table if necessary
     64     umtx_lock(&gZoneMetaLock);
     65     if (gUStringTable == NULL) {
     66         gUStringTable = (UChar**)uprv_malloc(USTRING_ALLOC_START*sizeof(UChar*));
     67         if (gUStringTable != NULL) {
     68             gUStringAlloc = USTRING_ALLOC_START;
     69         }
     70     }
     71     if (gUStringTable != NULL) {
     72         // expand the table if necessary
     73         if (gUStringCount == gUStringAlloc) {
     74             UChar ** newTable = (UChar**)uprv_realloc(gUStringTable, (gUStringAlloc+USTRING_ALLOC_INCR)*sizeof(UChar*));
     75             if (newTable != NULL) {
     76                 gUStringTable = newTable;
     77                 gUStringAlloc += USTRING_ALLOC_INCR;
     78             }
     79         }
     80         // add the string if possible
     81         if (gUStringCount < gUStringAlloc) {
     82             uStringSpace = (UChar*)uprv_malloc(uStringLen*sizeof(UChar));
     83             if (uStringSpace != NULL) {
     84                 gUStringTable[gUStringCount++] = uStringSpace;
     85             }
     86         }
     87     }
     88     umtx_unlock(&gZoneMetaLock);
     89     return uStringSpace;
     90 }
     91 
     92 static void removeLastUStringFromTable(void) {
     93 	umtx_lock(&gZoneMetaLock);
     94     if (gUStringCount > 0) {
     95         free(gUStringTable[--gUStringCount]);
     96     }
     97     umtx_unlock(&gZoneMetaLock);
     98 }
     99 
    100 static void freeUStringTable(void) {
    101     int32_t uStringCount = gUStringCount;
    102     gUStringCount = 0;
    103     gUStringAlloc = 0;
    104     if (gUStringTable != NULL) {
    105         while (uStringCount > 0) {
    106             free(gUStringTable[--uStringCount]);
    107         }
    108         free(gUStringTable);
    109         gUStringTable = NULL;
    110     }
    111 }
    112 
    113 /**
    114  * Cleanup callback func
    115  */
    116 static UBool U_CALLCONV zoneMeta_cleanup(void)
    117 {
    118      umtx_destroy(&gZoneMetaLock);
    119 
    120     if (gCanonicalMap != NULL) {
    121         uhash_close(gCanonicalMap);
    122         gCanonicalMap = NULL;
    123     }
    124     gCanonicalMapInitialized = FALSE;
    125 
    126     if (gOlsonToMeta != NULL) {
    127         uhash_close(gOlsonToMeta);
    128         gOlsonToMeta = NULL;
    129     }
    130     gOlsonToMetaInitialized = FALSE;
    131 
    132     if (gMetaToOlson != NULL) {
    133         uhash_close(gMetaToOlson);
    134         gMetaToOlson = NULL;
    135     }
    136     gMetaToOlsonInitialized = FALSE;
    137 
    138     freeUStringTable();
    139 
    140     return TRUE;
    141 }
    142 
    143 /**
    144  * Deleter for UChar* string
    145  */
    146 static void U_CALLCONV
    147 deleteUCharString(void *obj) {
    148     UChar *entry = (UChar*)obj;
    149     uprv_free(entry);
    150 }
    151 
    152 /**
    153  * Deleter for UVector
    154  */
    155 static void U_CALLCONV
    156 deleteUVector(void *obj) {
    157    delete (U_NAMESPACE_QUALIFIER UVector*) obj;
    158 }
    159 
    160 /**
    161  * Deleter for CanonicalMapEntry
    162  */
    163 static void U_CALLCONV
    164 deleteCanonicalMapEntry(void *obj) {
    165     U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj;
    166     uprv_free(entry);
    167 }
    168 
    169 /**
    170  * Deleter for OlsonToMetaMappingEntry
    171  */
    172 static void U_CALLCONV
    173 deleteOlsonToMetaMappingEntry(void *obj) {
    174     U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
    175     uprv_free(entry);
    176 }
    177 
    178 /**
    179  * Deleter for MetaToOlsonMappingEntry
    180  */
    181 static void U_CALLCONV
    182 deleteMetaToOlsonMappingEntry(void *obj) {
    183     U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj;
    184     uprv_free(entry->territory);
    185     uprv_free(entry);
    186 }
    187 U_CDECL_END
    188 
    189 U_NAMESPACE_BEGIN
    190 
    191 #define ZID_KEY_MAX 128
    192 static const char gZoneStringsTag[]     = "zoneStrings";
    193 static const char gUseMetazoneTag[]     = "um";
    194 
    195 static const char gSupplementalData[]   = "supplementalData";
    196 static const char gMapTimezonesTag[]    = "mapTimezones";
    197 static const char gMetazonesTag[]       = "metazones";
    198 static const char gZoneFormattingTag[]  = "zoneFormatting";
    199 static const char gCanonicalTag[]       = "canonical";
    200 static const char gTerritoryTag[]       = "territory";
    201 static const char gAliasesTag[]         = "aliases";
    202 static const char gMultizoneTag[]       = "multizone";
    203 
    204 static const char gMetazoneInfo[]       = "metazoneInfo";
    205 static const char gMetazoneMappings[]   = "metazoneMappings";
    206 
    207 #define MZID_PREFIX_LEN 5
    208 static const char gMetazoneIdPrefix[]   = "meta:";
    209 
    210 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
    211 
    212 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
    213 
    214 /*
    215  * Convert a date string used by metazone mappings to UDate.
    216  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
    217  */
    218 static UDate
    219 parseDate (const UChar *text, UErrorCode &status) {
    220     if (U_FAILURE(status)) {
    221         return 0;
    222     }
    223     int32_t len = u_strlen(text);
    224     if (len != 16 && len != 10) {
    225         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
    226         status = U_INVALID_FORMAT_ERROR;
    227         return 0;
    228     }
    229 
    230     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
    231     int32_t idx;
    232 
    233     // "yyyy" (0 - 3)
    234     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
    235         n = ASCII_DIGIT((int32_t)text[idx]);
    236         if (n >= 0) {
    237             year = 10*year + n;
    238         } else {
    239             status = U_INVALID_FORMAT_ERROR;
    240         }
    241     }
    242     // "MM" (5 - 6)
    243     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
    244         n = ASCII_DIGIT((int32_t)text[idx]);
    245         if (n >= 0) {
    246             month = 10*month + n;
    247         } else {
    248             status = U_INVALID_FORMAT_ERROR;
    249         }
    250     }
    251     // "dd" (8 - 9)
    252     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
    253         n = ASCII_DIGIT((int32_t)text[idx]);
    254         if (n >= 0) {
    255             day = 10*day + n;
    256         } else {
    257             status = U_INVALID_FORMAT_ERROR;
    258         }
    259     }
    260     if (len == 16) {
    261         // "HH" (11 - 12)
    262         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
    263             n = ASCII_DIGIT((int32_t)text[idx]);
    264             if (n >= 0) {
    265                 hour = 10*hour + n;
    266             } else {
    267                 status = U_INVALID_FORMAT_ERROR;
    268             }
    269         }
    270         // "mm" (14 - 15)
    271         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
    272             n = ASCII_DIGIT((int32_t)text[idx]);
    273             if (n >= 0) {
    274                 min = 10*min + n;
    275             } else {
    276                 status = U_INVALID_FORMAT_ERROR;
    277             }
    278         }
    279     }
    280 
    281     if (U_SUCCESS(status)) {
    282         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
    283             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
    284         return date;
    285     }
    286     return 0;
    287 }
    288 
    289 UHashtable*
    290 ZoneMeta::createCanonicalMap(void) {
    291     UErrorCode status = U_ZERO_ERROR;
    292 
    293     UHashtable *canonicalMap = NULL;
    294     UResourceBundle *zoneFormatting = NULL;
    295     UResourceBundle *tzitem = NULL;
    296     UResourceBundle *aliases = NULL;
    297 
    298     StringEnumeration* tzenum = NULL;
    299     int32_t numZones;
    300 
    301     canonicalMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    302     if (U_FAILURE(status)) {
    303         return NULL;
    304     }
    305     // no key deleter
    306     uhash_setValueDeleter(canonicalMap, deleteCanonicalMapEntry);
    307 
    308     zoneFormatting = ures_openDirect(NULL, gSupplementalData, &status);
    309     zoneFormatting = ures_getByKey(zoneFormatting, gZoneFormattingTag, zoneFormatting, &status);
    310     if (U_FAILURE(status)) {
    311         goto error_cleanup;
    312     }
    313 
    314     while (ures_hasNext(zoneFormatting)) {
    315         tzitem = ures_getNextResource(zoneFormatting, tzitem, &status);
    316         if (U_FAILURE(status)) {
    317             status = U_ZERO_ERROR;
    318             continue;
    319         }
    320         if (ures_getType(tzitem) != URES_TABLE) {
    321             continue;
    322         }
    323 
    324         int32_t canonicalLen;
    325         const UChar *canonical = ures_getStringByKey(tzitem, gCanonicalTag, &canonicalLen, &status);
    326         if (U_FAILURE(status)) {
    327             status = U_ZERO_ERROR;
    328             continue;
    329         }
    330 
    331         int32_t territoryLen;
    332         const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status);
    333         if (U_FAILURE(status)) {
    334             territory = NULL;
    335             status = U_ZERO_ERROR;
    336         }
    337 
    338         // Create canonical map entry
    339         CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
    340         if (entry == NULL) {
    341             status = U_MEMORY_ALLOCATION_ERROR;
    342             goto error_cleanup;
    343         }
    344         entry->id = canonical;
    345         if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
    346             entry->country = NULL;
    347         } else {
    348             entry->country = territory;
    349         }
    350 
    351         // Put this entry in the hashtable. Since this hashtable has no key deleter,
    352         // key is treated as const, but must be passed as non-const.
    353         uhash_put(canonicalMap, (UChar*)canonical, entry, &status);
    354         if (U_FAILURE(status)) {
    355             goto error_cleanup;
    356         }
    357 
    358         // Get aliases
    359         aliases = ures_getByKey(tzitem, gAliasesTag, aliases, &status);
    360         if (U_FAILURE(status)) {
    361             // No aliases
    362             status = U_ZERO_ERROR;
    363             continue;
    364         }
    365 
    366         while (ures_hasNext(aliases)) {
    367             const UChar* alias = ures_getNextString(aliases, NULL, NULL, &status);
    368             if (U_FAILURE(status)) {
    369                 status = U_ZERO_ERROR;
    370                 continue;
    371             }
    372             // Create canonical map entry for this alias
    373             entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
    374             if (entry == NULL) {
    375                 status = U_MEMORY_ALLOCATION_ERROR;
    376                 goto error_cleanup;
    377             }
    378             entry->id = canonical;
    379             if (territory  == NULL || u_strcmp(territory, gWorld) == 0) {
    380                 entry->country = NULL;
    381             } else {
    382                 entry->country = territory;
    383             }
    384 
    385             // Put this entry in the hashtable. Since this hashtable has no key deleter,
    386             // key is treated as const, but must be passed as non-const.
    387             uhash_put(canonicalMap, (UChar*)alias, entry, &status);
    388             if (U_FAILURE(status)) {
    389                 goto error_cleanup;
    390             }
    391         }
    392     }
    393 
    394     // Some available Olson zones are not included in CLDR data (such as Asia/Riyadh87).
    395     // Also, when we update Olson tzdata, new zones may be added.
    396     // This code scans all available zones in zoneinfo.res, and if any of them are
    397     // missing, add them to the map.
    398     tzenum = TimeZone::createEnumeration();
    399     numZones = tzenum->count(status);
    400     if (U_SUCCESS(status)) {
    401         int32_t i;
    402         for (i = 0; i < numZones; i++) {
    403             const UnicodeString *zone = tzenum->snext(status);
    404             if (U_FAILURE(status)) {
    405                 // We should not get here.
    406                 status = U_ZERO_ERROR;
    407                 continue;
    408             }
    409             UChar zoneUChars[ZID_KEY_MAX];
    410             int32_t zoneUCharsLen = zone->extract(zoneUChars, ZID_KEY_MAX, status) + 1; // Add one for NUL termination
    411             if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) {
    412                 status = U_ZERO_ERROR;
    413                 continue; // zone id is too long to extract
    414             }
    415             CanonicalMapEntry *entry = (CanonicalMapEntry*)uhash_get(canonicalMap, zoneUChars);
    416             if (entry) {
    417                 // Already included in CLDR data
    418                 continue;
    419             }
    420             // Not in CLDR data, but it could be new one whose alias is available
    421             // in CLDR.
    422             int32_t nTzdataEquivalent = TimeZone::countEquivalentIDs(*zone);
    423             int32_t j;
    424             for (j = 0; j < nTzdataEquivalent; j++) {
    425                 UnicodeString alias = TimeZone::getEquivalentID(*zone, j);
    426                 if (alias == *zone) {
    427                     continue;
    428                 }
    429                 UChar aliasUChars[ZID_KEY_MAX];
    430                 alias.extract(aliasUChars, ZID_KEY_MAX, status);
    431                 if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) {
    432                     status = U_ZERO_ERROR;
    433                     continue; // zone id is too long to extract
    434                 }
    435                 entry = (CanonicalMapEntry*)uhash_get(canonicalMap, aliasUChars);
    436                 if (entry != NULL) {
    437                     break;
    438                 }
    439             }
    440             // Create a new map entry
    441             CanonicalMapEntry* newEntry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
    442             int32_t idLen;
    443             if (newEntry == NULL) {
    444                 status = U_MEMORY_ALLOCATION_ERROR;
    445                 goto error_cleanup;
    446             }
    447             if (entry == NULL) {
    448                 // Set dereferenced zone ID as the canonical ID
    449                 UnicodeString derefZone;
    450                 TimeZone::dereferOlsonLink(*zone, derefZone);
    451                 if (derefZone.length() == 0) {
    452                     // It should never happen.. but just in case
    453                     derefZone = *zone;
    454                 }
    455                 idLen = derefZone.length() + 1;
    456                 newEntry->id = allocUStringInTable(idLen);
    457                 if (newEntry->id == NULL) {
    458                     status = U_MEMORY_ALLOCATION_ERROR;
    459                     uprv_free(newEntry);
    460                     goto error_cleanup;
    461                 }
    462                 // Copy NULL terminated string
    463                 derefZone.extract((UChar*)(newEntry->id), idLen, status);
    464                 if (U_FAILURE(status)) {
    465                     removeLastUStringFromTable();
    466                     uprv_free(newEntry);
    467                     goto error_cleanup;
    468                 }
    469                 // No territory information available
    470                 newEntry->country = NULL;
    471             } else {
    472                 // Duplicate the entry
    473                 newEntry->id = entry->id;
    474                 newEntry->country = entry->country;
    475             }
    476 
    477             // Put this entry in the hashtable
    478             UChar *key = allocUStringInTable(zoneUCharsLen);
    479             if (key == NULL) {
    480                 status = U_MEMORY_ALLOCATION_ERROR;
    481                 deleteCanonicalMapEntry(newEntry);
    482                 goto error_cleanup;
    483             }
    484             u_strncpy(key, zoneUChars, zoneUCharsLen);
    485             uhash_put(canonicalMap, key, newEntry, &status);
    486             if (U_FAILURE(status)) {
    487                 goto error_cleanup;
    488             }
    489         }
    490     }
    491 
    492 normal_cleanup:
    493     ures_close(aliases);
    494     ures_close(tzitem);
    495     ures_close(zoneFormatting);
    496     delete tzenum;
    497     return canonicalMap;
    498 
    499 error_cleanup:
    500     if (canonicalMap != NULL) {
    501         uhash_close(canonicalMap);
    502         canonicalMap = NULL;
    503     }
    504     goto normal_cleanup;
    505 }
    506 
    507 /*
    508  * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
    509  */
    510 UHashtable*
    511 ZoneMeta::createOlsonToMetaMap(void) {
    512     UErrorCode status = U_ZERO_ERROR;
    513 
    514     UHashtable *olsonToMeta = NULL;
    515     UResourceBundle *metazoneMappings = NULL;
    516     UResourceBundle *zoneItem = NULL;
    517     UResourceBundle *mz = NULL;
    518     StringEnumeration *tzids = NULL;
    519 
    520     olsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    521     if (U_FAILURE(status)) {
    522         return NULL;
    523     }
    524     uhash_setKeyDeleter(olsonToMeta, deleteUCharString);
    525     uhash_setValueDeleter(olsonToMeta, deleteUVector);
    526 
    527     // Read metazone mappings from metazoneInfo bundle
    528     metazoneMappings = ures_openDirect(NULL, gMetazoneInfo, &status);
    529     metazoneMappings = ures_getByKey(metazoneMappings, gMetazoneMappings, metazoneMappings, &status);
    530     if (U_FAILURE(status)) {
    531         goto error_cleanup;
    532     }
    533 
    534     // Walk through all canonical tzids
    535     char zidkey[ZID_KEY_MAX];
    536 
    537     tzids = TimeZone::createEnumeration();
    538     const UnicodeString *tzid;
    539     while ((tzid = tzids->snext(status))) {
    540         if (U_FAILURE(status)) {
    541             goto error_cleanup;
    542         }
    543         // We may skip aliases, because the bundle
    544         // contains only canonical IDs.  For now, try
    545         // all of them.
    546         tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
    547         zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
    548 
    549         // Replace '/' with ':'
    550         UBool foundSep = FALSE;
    551         char *p = zidkey;
    552         while (*p) {
    553             if (*p == '/') {
    554                 *p = ':';
    555                 foundSep = TRUE;
    556             }
    557             p++;
    558         }
    559         if (!foundSep) {
    560             // A valid time zone key has at least one separator
    561             continue;
    562         }
    563 
    564         zoneItem = ures_getByKey(metazoneMappings, zidkey, zoneItem, &status);
    565         if (U_FAILURE(status)) {
    566             status = U_ZERO_ERROR;
    567             continue;
    568         }
    569 
    570         UVector *mzMappings = NULL;
    571         while (ures_hasNext(zoneItem)) {
    572             mz = ures_getNextResource(zoneItem, mz, &status);
    573             const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
    574             const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
    575             const UChar *mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
    576 
    577             if(U_FAILURE(status)){
    578                 status = U_ZERO_ERROR;
    579                 continue;
    580             }
    581             // We do not want to use SimpleDateformat to parse boundary dates,
    582             // because this code could be triggered by the initialization code
    583             // used by SimpleDateFormat.
    584             UDate from = parseDate(mz_from, status);
    585             UDate to = parseDate(mz_to, status);
    586             if (U_FAILURE(status)) {
    587                 status = U_ZERO_ERROR;
    588                 continue;
    589             }
    590 
    591             OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
    592             if (entry == NULL) {
    593                 status = U_MEMORY_ALLOCATION_ERROR;
    594                 break;
    595             }
    596             entry->mzid = mz_name;
    597             entry->from = from;
    598             entry->to = to;
    599 
    600             if (mzMappings == NULL) {
    601                 mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
    602                 if (U_FAILURE(status)) {
    603                     delete mzMappings;
    604                     deleteOlsonToMetaMappingEntry(entry);
    605                     uprv_free(entry);
    606                     break;
    607                 }
    608             }
    609 
    610             mzMappings->addElement(entry, status);
    611             if (U_FAILURE(status)) {
    612                 break;
    613             }
    614         }
    615 
    616         if (U_FAILURE(status)) {
    617             if (mzMappings != NULL) {
    618                 delete mzMappings;
    619             }
    620             goto error_cleanup;
    621         }
    622         if (mzMappings != NULL) {
    623             // Add to hashtable
    624             int32_t tzidLen = tzid->length() + 1; // Add one for NUL terminator
    625             UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
    626             if (key == NULL) {
    627                 status = U_MEMORY_ALLOCATION_ERROR;
    628                 delete mzMappings;
    629                 goto error_cleanup;
    630             }
    631             tzid->extract(key, tzidLen, status);
    632             uhash_put(olsonToMeta, key, mzMappings, &status);
    633             if (U_FAILURE(status)) {
    634                 goto error_cleanup;
    635             }
    636         }
    637     }
    638 
    639 normal_cleanup:
    640     if (tzids != NULL) {
    641         delete tzids;
    642     }
    643     ures_close(zoneItem);
    644     ures_close(mz);
    645     ures_close(metazoneMappings);
    646     return olsonToMeta;
    647 
    648 error_cleanup:
    649     if (olsonToMeta != NULL) {
    650         uhash_close(olsonToMeta);
    651         olsonToMeta = NULL;
    652     }
    653     goto normal_cleanup;
    654 }
    655 
    656 UHashtable*
    657 ZoneMeta::createMetaToOlsonMap(void) {
    658     UErrorCode status = U_ZERO_ERROR;
    659 
    660     UHashtable *metaToOlson = NULL;
    661     UResourceBundle *metazones = NULL;
    662     UResourceBundle *mz = NULL;
    663 
    664     metaToOlson = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    665     if (U_FAILURE(status)) {
    666         return NULL;
    667     }
    668     uhash_setKeyDeleter(metaToOlson, deleteUCharString);
    669     uhash_setValueDeleter(metaToOlson, deleteUVector);
    670 
    671     metazones = ures_openDirect(NULL, gSupplementalData, &status);
    672     metazones = ures_getByKey(metazones, gMapTimezonesTag, metazones, &status);
    673     metazones = ures_getByKey(metazones, gMetazonesTag, metazones, &status);
    674     if (U_FAILURE(status)) {
    675         goto error_cleanup;
    676     }
    677 
    678     while (ures_hasNext(metazones)) {
    679         mz = ures_getNextResource(metazones, mz, &status);
    680         if (U_FAILURE(status)) {
    681             status = U_ZERO_ERROR;
    682             continue;
    683         }
    684         const char *mzkey = ures_getKey(mz);
    685         if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) {
    686             const char *mzid = mzkey + MZID_PREFIX_LEN;
    687             const char *territory = uprv_strrchr(mzid, '_');
    688             int32_t mzidLen = 0;
    689             int32_t territoryLen = 0;
    690             if (territory) {
    691                 mzidLen = territory - mzid;
    692                 territory++;
    693                 territoryLen = uprv_strlen(territory);
    694             }
    695             if (mzidLen > 0 && territoryLen > 0) {
    696                 int32_t tzidLen;
    697                 const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status);
    698                 if (U_SUCCESS(status)) {
    699                     // Create MetaToOlsonMappingEntry
    700                     MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry));
    701                     if (entry == NULL) {
    702                         status = U_MEMORY_ALLOCATION_ERROR;
    703                         goto error_cleanup;
    704                     }
    705                     entry->id = tzid;
    706                     entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
    707                     if (entry->territory == NULL) {
    708                         status = U_MEMORY_ALLOCATION_ERROR;
    709                         uprv_free(entry);
    710                         goto error_cleanup;
    711                     }
    712                     u_charsToUChars(territory, entry->territory, territoryLen + 1);
    713 
    714                     // Check if mapping entries for metazone is already available
    715                     if (mzidLen < ZID_KEY_MAX) {
    716                         UChar mzidUChars[ZID_KEY_MAX];
    717                         u_charsToUChars(mzid, mzidUChars, mzidLen);
    718                         mzidUChars[mzidLen++] = 0; // Add NUL terminator
    719                         UVector *tzMappings = (UVector*)uhash_get(metaToOlson, mzidUChars);
    720                         if (tzMappings == NULL) {
    721                             // Create new UVector and put it into the hashtable
    722                             tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status);
    723                             if (U_FAILURE(status)) {
    724                                 deleteMetaToOlsonMappingEntry(entry);
    725                                 goto error_cleanup;
    726                             }
    727                             UChar *key = (UChar*)uprv_malloc(mzidLen * sizeof(UChar));
    728                             if (key == NULL) {
    729                                 status = U_MEMORY_ALLOCATION_ERROR;
    730                                 delete tzMappings;
    731                                 deleteMetaToOlsonMappingEntry(entry);
    732                                 goto error_cleanup;
    733                             }
    734                             u_strncpy(key, mzidUChars, mzidLen);
    735                             uhash_put(metaToOlson, key, tzMappings, &status);
    736                             if (U_FAILURE(status)) {
    737                                 goto error_cleanup;
    738                             }
    739                         }
    740                         tzMappings->addElement(entry, status);
    741                         if (U_FAILURE(status)) {
    742                             goto error_cleanup;
    743                         }
    744                     } else {
    745                         deleteMetaToOlsonMappingEntry(entry);
    746                     }
    747                 } else {
    748                     status = U_ZERO_ERROR;
    749                 }
    750             }
    751         }
    752     }
    753 
    754 normal_cleanup:
    755     ures_close(mz);
    756     ures_close(metazones);
    757     return metaToOlson;
    758 
    759 error_cleanup:
    760     if (metaToOlson != NULL) {
    761         uhash_close(metaToOlson);
    762         metaToOlson = NULL;
    763     }
    764     goto normal_cleanup;
    765 }
    766 
    767  /*
    768  * Initialize global objects
    769  */
    770 void
    771 ZoneMeta::initializeCanonicalMap(void) {
    772     UBool initialized;
    773     UMTX_CHECK(&gZoneMetaLock, gCanonicalMapInitialized, initialized);
    774     if (initialized) {
    775         return;
    776     }
    777     // Initialize hash table
    778     UHashtable *tmpCanonicalMap = createCanonicalMap();
    779 
    780     umtx_lock(&gZoneMetaLock);
    781     if (!gCanonicalMapInitialized) {
    782         gCanonicalMap = tmpCanonicalMap;
    783         tmpCanonicalMap = NULL;
    784         gCanonicalMapInitialized = TRUE;
    785     }
    786     umtx_unlock(&gZoneMetaLock);
    787 
    788     // OK to call the following multiple times with the same function
    789     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    790     if (tmpCanonicalMap != NULL) {
    791         uhash_close(tmpCanonicalMap);
    792     }
    793 }
    794 
    795 void
    796 ZoneMeta::initializeOlsonToMeta(void) {
    797     UBool initialized;
    798     UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
    799     if (initialized) {
    800         return;
    801     }
    802     // Initialize hash tables
    803     UHashtable *tmpOlsonToMeta = createOlsonToMetaMap();
    804 
    805     umtx_lock(&gZoneMetaLock);
    806     if (!gOlsonToMetaInitialized) {
    807         gOlsonToMeta = tmpOlsonToMeta;
    808         tmpOlsonToMeta = NULL;
    809         gOlsonToMetaInitialized = TRUE;
    810     }
    811     umtx_unlock(&gZoneMetaLock);
    812 
    813     // OK to call the following multiple times with the same function
    814     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    815     if (tmpOlsonToMeta != NULL) {
    816         uhash_close(tmpOlsonToMeta);
    817     }
    818 }
    819 
    820 void
    821 ZoneMeta::initializeMetaToOlson(void) {
    822     UBool initialized;
    823     UMTX_CHECK(&gZoneMetaLock, gMetaToOlsonInitialized, initialized);
    824     if (initialized) {
    825         return;
    826     }
    827     // Initialize hash table
    828     UHashtable *tmpMetaToOlson = createMetaToOlsonMap();
    829 
    830     umtx_lock(&gZoneMetaLock);
    831     if (!gMetaToOlsonInitialized) {
    832         gMetaToOlson = tmpMetaToOlson;
    833         tmpMetaToOlson = NULL;
    834         gMetaToOlsonInitialized = TRUE;
    835     }
    836     umtx_unlock(&gZoneMetaLock);
    837 
    838     // OK to call the following multiple times with the same function
    839     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    840     if (tmpMetaToOlson != NULL) {
    841         uhash_close(tmpMetaToOlson);
    842     }
    843 }
    844 
    845 UnicodeString& U_EXPORT2
    846 ZoneMeta::getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
    847     const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
    848     if (entry != NULL) {
    849         systemID.setTo(entry->id);
    850     } else {
    851         status = U_ILLEGAL_ARGUMENT_ERROR;
    852     }
    853     return systemID;
    854 }
    855 
    856 UnicodeString& U_EXPORT2
    857 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
    858     const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
    859     if (entry != NULL && entry->country != NULL) {
    860         canonicalCountry.setTo(entry->country);
    861     } else {
    862         // Use the input tzid
    863         canonicalCountry.remove();
    864     }
    865     return canonicalCountry;
    866 }
    867 
    868 const CanonicalMapEntry* U_EXPORT2
    869 ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) {
    870     initializeCanonicalMap();
    871     CanonicalMapEntry *entry = NULL;
    872     if (gCanonicalMap != NULL) {
    873         UErrorCode status = U_ZERO_ERROR;
    874         UChar tzidUChars[ZID_KEY_MAX];
    875         tzid.extract(tzidUChars, ZID_KEY_MAX, status);
    876         if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
    877             entry = (CanonicalMapEntry*)uhash_get(gCanonicalMap, tzidUChars);
    878         }
    879     }
    880     return entry;
    881 }
    882 
    883 UnicodeString& U_EXPORT2
    884 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
    885     UErrorCode status = U_ZERO_ERROR;
    886 
    887     // Get canonical country for the zone
    888     getCanonicalCountry(tzid, country);
    889 
    890     if (!country.isEmpty()) {
    891         UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
    892         UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
    893         UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
    894 
    895         if (U_SUCCESS(status)) {
    896             while (ures_hasNext(multizone)) {
    897                 int32_t len;
    898                 const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status);
    899                 if (country.compare(multizoneCountry, len) == 0) {
    900                     // Included in the multizone country list
    901                     country.remove();
    902                     break;
    903                 }
    904             }
    905         }
    906 
    907         ures_close(multizone);
    908         ures_close(zoneFormatting);
    909         ures_close(supplementalDataBundle);
    910     }
    911 
    912     return country;
    913 }
    914 
    915 UnicodeString& U_EXPORT2
    916 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
    917     UBool isSet = FALSE;
    918     const UVector *mappings = getMetazoneMappings(tzid);
    919     if (mappings != NULL) {
    920         for (int32_t i = 0; i < mappings->size(); i++) {
    921             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
    922             if (mzm->from <= date && mzm->to > date) {
    923                 result.setTo(mzm->mzid, -1);
    924                 isSet = TRUE;
    925                 break;
    926             }
    927         }
    928     }
    929     if (!isSet) {
    930         result.remove();
    931     }
    932     return result;
    933 }
    934 
    935 const UVector* U_EXPORT2
    936 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
    937     initializeOlsonToMeta();
    938     const UVector *result = NULL;
    939     if (gOlsonToMeta != NULL) {
    940         UErrorCode status = U_ZERO_ERROR;
    941         UChar tzidUChars[ZID_KEY_MAX];
    942         tzid.extract(tzidUChars, ZID_KEY_MAX, status);
    943         if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
    944             result = (UVector*)uhash_get(gOlsonToMeta, tzidUChars);
    945         }
    946     }
    947     return result;
    948 }
    949 
    950 UnicodeString& U_EXPORT2
    951 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
    952     initializeMetaToOlson();
    953     UBool isSet = FALSE;
    954     if (gMetaToOlson != NULL) {
    955         UErrorCode status = U_ZERO_ERROR;
    956         UChar mzidUChars[ZID_KEY_MAX];
    957         mzid.extract(mzidUChars, ZID_KEY_MAX, status);
    958         if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
    959             UVector *mappings = (UVector*)uhash_get(gMetaToOlson, mzidUChars);
    960             if (mappings != NULL) {
    961                 // Find a preferred time zone for the given region.
    962                 for (int32_t i = 0; i < mappings->size(); i++) {
    963                     MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i);
    964                     if (region.compare(olsonmap->territory, -1) == 0) {
    965                         result.setTo(olsonmap->id);
    966                         isSet = TRUE;
    967                         break;
    968                     } else if (u_strcmp(olsonmap->territory, gWorld) == 0) {
    969                         result.setTo(olsonmap->id);
    970                         isSet = TRUE;
    971                     }
    972                 }
    973             }
    974         }
    975     }
    976     if (!isSet) {
    977         result.remove();
    978     }
    979     return result;
    980 }
    981 
    982 
    983 U_NAMESPACE_END
    984 
    985 #endif /* #if !UCONFIG_NO_FORMATTING */
    986