]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/zonemeta.cpp
ICU-400.40.tar.gz
[apple/icu.git] / icuSources / i18n / zonemeta.cpp
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 */