]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tznames_impl.cpp
ICU-59180.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 2011-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 * File TZNAMES_IMPL.CPP
10 *
11 *******************************************************************************
12 */
13
14 #include "unicode/utypes.h"
15
16 #if !UCONFIG_NO_FORMATTING
17
18 #include "unicode/strenum.h"
19 #include "unicode/ustring.h"
20 #include "unicode/timezone.h"
21
22 #include "tznames_impl.h"
23 #include "cmemory.h"
24 #include "cstring.h"
25 #include "uassert.h"
26 #include "mutex.h"
27 #include "resource.h"
28 #include "uresimp.h"
29 #include "ureslocs.h"
30 #include "zonemeta.h"
31 #include "ucln_in.h"
32 #include "uvector.h"
33 #include "olsontz.h"
34
35 U_NAMESPACE_BEGIN
36
37 #define ZID_KEY_MAX 128
38 #define MZ_PREFIX_LEN 5
39
40 static const char gZoneStrings[] = "zoneStrings";
41 static const char gMZPrefix[] = "meta:";
42
43 static const char EMPTY[] = "<empty>"; // place holder for empty ZNames
44 static const char DUMMY_LOADER[] = "<dummy>"; // place holder for dummy ZNamesLoader
45 static const UChar NO_NAME[] = { 0 }; // for empty no-fallback time zone names
46
47 // stuff for TZDBTimeZoneNames
48 static const char* TZDBNAMES_KEYS[] = {"ss", "sd"};
49 static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
50
51 static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER;
52 static UMutex gDataMutex = U_MUTEX_INITIALIZER;
53
54 static UHashtable* gTZDBNamesMap = NULL;
55 static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
56
57 static TextTrieMap* gTZDBNamesTrie = NULL;
58 static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER;
59
60 // The order in which strings are stored may be different than the order in the public enum.
61 enum UTimeZoneNameTypeIndex {
62 UTZNM_INDEX_UNKNOWN = -1,
63 UTZNM_INDEX_EXEMPLAR_LOCATION,
64 UTZNM_INDEX_LONG_GENERIC,
65 UTZNM_INDEX_LONG_STANDARD,
66 UTZNM_INDEX_LONG_DAYLIGHT,
67 UTZNM_INDEX_SHORT_GENERIC,
68 UTZNM_INDEX_SHORT_STANDARD,
69 UTZNM_INDEX_SHORT_DAYLIGHT,
70 UTZNM_INDEX_COUNT
71 };
72 static const UChar* EMPTY_NAMES[UTZNM_INDEX_COUNT] = {0,0,0,0,0,0,0};
73
74 U_CDECL_BEGIN
75 static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) {
76 if (gTZDBNamesMap != NULL) {
77 uhash_close(gTZDBNamesMap);
78 gTZDBNamesMap = NULL;
79 }
80 gTZDBNamesMapInitOnce.reset();
81
82 if (gTZDBNamesTrie != NULL) {
83 delete gTZDBNamesTrie;
84 gTZDBNamesTrie = NULL;
85 }
86 gTZDBNamesTrieInitOnce.reset();
87
88 return TRUE;
89 }
90 U_CDECL_END
91
92 /**
93 * ZNameInfo stores zone name information in the trie
94 */
95 struct ZNameInfo {
96 UTimeZoneNameType type;
97 const UChar* tzID;
98 const UChar* mzID;
99 };
100
101 /**
102 * ZMatchInfo stores zone name match information used by find method
103 */
104 struct ZMatchInfo {
105 const ZNameInfo* znameInfo;
106 int32_t matchLength;
107 };
108
109 // Helper functions
110 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result);
111
112 #define DEFAULT_CHARACTERNODE_CAPACITY 1
113
114 // ---------------------------------------------------
115 // CharacterNode class implementation
116 // ---------------------------------------------------
117 void CharacterNode::clear() {
118 uprv_memset(this, 0, sizeof(*this));
119 }
120
121 void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
122 if (fValues == NULL) {
123 // Do nothing.
124 } else if (!fHasValuesVector) {
125 if (valueDeleter) {
126 valueDeleter(fValues);
127 }
128 } else {
129 delete (UVector *)fValues;
130 }
131 }
132
133 void
134 CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
135 if (U_FAILURE(status)) {
136 if (valueDeleter) {
137 valueDeleter(value);
138 }
139 return;
140 }
141 if (fValues == NULL) {
142 fValues = value;
143 } else {
144 // At least one value already.
145 if (!fHasValuesVector) {
146 // There is only one value so far, and not in a vector yet.
147 // Create a vector and add the old value.
148 UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
149 if (U_FAILURE(status)) {
150 if (valueDeleter) {
151 valueDeleter(value);
152 }
153 return;
154 }
155 values->addElement(fValues, status);
156 fValues = values;
157 fHasValuesVector = TRUE;
158 }
159 // Add the new value.
160 ((UVector *)fValues)->addElement(value, status);
161 }
162 }
163
164 // ---------------------------------------------------
165 // TextTrieMapSearchResultHandler class implementation
166 // ---------------------------------------------------
167 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
168 }
169
170 // ---------------------------------------------------
171 // TextTrieMap class implementation
172 // ---------------------------------------------------
173 TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
174 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
175 fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
176 }
177
178 TextTrieMap::~TextTrieMap() {
179 int32_t index;
180 for (index = 0; index < fNodesCount; ++index) {
181 fNodes[index].deleteValues(fValueDeleter);
182 }
183 uprv_free(fNodes);
184 if (fLazyContents != NULL) {
185 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
186 if (fValueDeleter) {
187 fValueDeleter(fLazyContents->elementAt(i+1));
188 }
189 }
190 delete fLazyContents;
191 }
192 }
193
194 int32_t TextTrieMap::isEmpty() const {
195 // Use a separate field for fIsEmpty because it will remain unchanged once the
196 // Trie is built, while fNodes and fLazyContents change with the lazy init
197 // of the nodes structure. Trying to test the changing fields has
198 // thread safety complications.
199 return fIsEmpty;
200 }
201
202
203 // We defer actually building the TextTrieMap node structure until the first time a
204 // search is performed. put() simply saves the parameters in case we do
205 // eventually need to build it.
206 //
207 void
208 TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
209 const UChar *s = sp.get(key, status);
210 put(s, value, status);
211 }
212
213 // This method is designed for a persistent key, such as string key stored in
214 // resource bundle.
215 void
216 TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
217 fIsEmpty = FALSE;
218 if (fLazyContents == NULL) {
219 fLazyContents = new UVector(status);
220 if (fLazyContents == NULL) {
221 status = U_MEMORY_ALLOCATION_ERROR;
222 }
223 }
224 if (U_FAILURE(status)) {
225 if (fValueDeleter) {
226 fValueDeleter((void*) key);
227 }
228 return;
229 }
230 U_ASSERT(fLazyContents != NULL);
231
232 UChar *s = const_cast<UChar *>(key);
233 fLazyContents->addElement(s, status);
234 if (U_FAILURE(status)) {
235 if (fValueDeleter) {
236 fValueDeleter((void*) key);
237 }
238 return;
239 }
240
241 fLazyContents->addElement(value, status);
242 }
243
244 void
245 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
246 if (fNodes == NULL) {
247 fNodesCapacity = 512;
248 fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
249 if (fNodes == NULL) {
250 status = U_MEMORY_ALLOCATION_ERROR;
251 return;
252 }
253 fNodes[0].clear(); // Init root node.
254 fNodesCount = 1;
255 }
256
257 UnicodeString foldedKey;
258 const UChar *keyBuffer;
259 int32_t keyLength;
260 if (fIgnoreCase) {
261 // Ok to use fastCopyFrom() because we discard the copy when we return.
262 foldedKey.fastCopyFrom(key).foldCase();
263 keyBuffer = foldedKey.getBuffer();
264 keyLength = foldedKey.length();
265 } else {
266 keyBuffer = key.getBuffer();
267 keyLength = key.length();
268 }
269
270 CharacterNode *node = fNodes;
271 int32_t index;
272 for (index = 0; index < keyLength; ++index) {
273 node = addChildNode(node, keyBuffer[index], status);
274 }
275 node->addValue(value, fValueDeleter, status);
276 }
277
278 UBool
279 TextTrieMap::growNodes() {
280 if (fNodesCapacity == 0xffff) {
281 return FALSE; // We use 16-bit node indexes.
282 }
283 int32_t newCapacity = fNodesCapacity + 1000;
284 if (newCapacity > 0xffff) {
285 newCapacity = 0xffff;
286 }
287 CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
288 if (newNodes == NULL) {
289 return FALSE;
290 }
291 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
292 uprv_free(fNodes);
293 fNodes = newNodes;
294 fNodesCapacity = newCapacity;
295 return TRUE;
296 }
297
298 CharacterNode*
299 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
300 if (U_FAILURE(status)) {
301 return NULL;
302 }
303 // Linear search of the sorted list of children.
304 uint16_t prevIndex = 0;
305 uint16_t nodeIndex = parent->fFirstChild;
306 while (nodeIndex > 0) {
307 CharacterNode *current = fNodes + nodeIndex;
308 UChar childCharacter = current->fCharacter;
309 if (childCharacter == c) {
310 return current;
311 } else if (childCharacter > c) {
312 break;
313 }
314 prevIndex = nodeIndex;
315 nodeIndex = current->fNextSibling;
316 }
317
318 // Ensure capacity. Grow fNodes[] if needed.
319 if (fNodesCount == fNodesCapacity) {
320 int32_t parentIndex = (int32_t)(parent - fNodes);
321 if (!growNodes()) {
322 status = U_MEMORY_ALLOCATION_ERROR;
323 return NULL;
324 }
325 parent = fNodes + parentIndex;
326 }
327
328 // Insert a new child node with c in sorted order.
329 CharacterNode *node = fNodes + fNodesCount;
330 node->clear();
331 node->fCharacter = c;
332 node->fNextSibling = nodeIndex;
333 if (prevIndex == 0) {
334 parent->fFirstChild = (uint16_t)fNodesCount;
335 } else {
336 fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
337 }
338 ++fNodesCount;
339 return node;
340 }
341
342 CharacterNode*
343 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
344 // Linear search of the sorted list of children.
345 uint16_t nodeIndex = parent->fFirstChild;
346 while (nodeIndex > 0) {
347 CharacterNode *current = fNodes + nodeIndex;
348 UChar childCharacter = current->fCharacter;
349 if (childCharacter == c) {
350 return current;
351 } else if (childCharacter > c) {
352 break;
353 }
354 nodeIndex = current->fNextSibling;
355 }
356 return NULL;
357 }
358
359 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
360 static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
361
362 // buildTrie() - The Trie node structure is needed. Create it from the data that was
363 // saved at the time the ZoneStringFormatter was created. The Trie is only
364 // needed for parsing operations, which are less common than formatting,
365 // and the Trie is big, which is why its creation is deferred until first use.
366 void TextTrieMap::buildTrie(UErrorCode &status) {
367 if (fLazyContents != NULL) {
368 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
369 const UChar *key = (UChar *)fLazyContents->elementAt(i);
370 void *val = fLazyContents->elementAt(i+1);
371 UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor.
372 putImpl(keyString, val, status);
373 }
374 delete fLazyContents;
375 fLazyContents = NULL;
376 }
377 }
378
379 void
380 TextTrieMap::search(const UnicodeString &text, int32_t start,
381 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
382 {
383 // TODO: if locking the mutex for each check proves to be a performance problem,
384 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
385 // the ICU atomic safe functions for assigning and testing.
386 // Don't test the pointer fLazyContents.
387 // Don't do unless it's really required.
388 Mutex lock(&TextTrieMutex);
389 if (fLazyContents != NULL) {
390 TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
391 nonConstThis->buildTrie(status);
392 }
393 }
394 if (fNodes == NULL) {
395 return;
396 }
397 search(fNodes, text, start, start, handler, status);
398 }
399
400 void
401 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
402 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
403 if (U_FAILURE(status)) {
404 return;
405 }
406 if (node->hasValues()) {
407 if (!handler->handleMatch(index - start, node, status)) {
408 return;
409 }
410 if (U_FAILURE(status)) {
411 return;
412 }
413 }
414 UChar32 c = text.char32At(index);
415 if (fIgnoreCase) {
416 // size of character may grow after fold operation
417 UnicodeString tmp(c);
418 tmp.foldCase();
419 int32_t tmpidx = 0;
420 while (tmpidx < tmp.length()) {
421 c = tmp.char32At(tmpidx);
422 node = getChildNode(node, c);
423 if (node == NULL) {
424 break;
425 }
426 tmpidx = tmp.moveIndex32(tmpidx, 1);
427 }
428 } else {
429 node = getChildNode(node, c);
430 }
431 if (node != NULL) {
432 search(node, text, start, index+1, handler, status);
433 }
434 }
435
436 // ---------------------------------------------------
437 // ZNStringPool class implementation
438 // ---------------------------------------------------
439 static const int32_t POOL_CHUNK_SIZE = 2000;
440 struct ZNStringPoolChunk: public UMemory {
441 ZNStringPoolChunk *fNext; // Ptr to next pool chunk
442 int32_t fLimit; // Index to start of unused area at end of fStrings
443 UChar fStrings[POOL_CHUNK_SIZE]; // Strings array
444 ZNStringPoolChunk();
445 };
446
447 ZNStringPoolChunk::ZNStringPoolChunk() {
448 fNext = NULL;
449 fLimit = 0;
450 }
451
452 ZNStringPool::ZNStringPool(UErrorCode &status) {
453 fChunks = NULL;
454 fHash = NULL;
455 if (U_FAILURE(status)) {
456 return;
457 }
458 fChunks = new ZNStringPoolChunk;
459 if (fChunks == NULL) {
460 status = U_MEMORY_ALLOCATION_ERROR;
461 return;
462 }
463
464 fHash = uhash_open(uhash_hashUChars /* keyHash */,
465 uhash_compareUChars /* keyComp */,
466 uhash_compareUChars /* valueComp */,
467 &status);
468 if (U_FAILURE(status)) {
469 return;
470 }
471 }
472
473 ZNStringPool::~ZNStringPool() {
474 if (fHash != NULL) {
475 uhash_close(fHash);
476 fHash = NULL;
477 }
478
479 while (fChunks != NULL) {
480 ZNStringPoolChunk *nextChunk = fChunks->fNext;
481 delete fChunks;
482 fChunks = nextChunk;
483 }
484 }
485
486 static const UChar EmptyString = 0;
487
488 const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
489 const UChar *pooledString;
490 if (U_FAILURE(status)) {
491 return &EmptyString;
492 }
493
494 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
495 if (pooledString != NULL) {
496 return pooledString;
497 }
498
499 int32_t length = u_strlen(s);
500 int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
501 if (remainingLength <= length) {
502 U_ASSERT(length < POOL_CHUNK_SIZE);
503 if (length >= POOL_CHUNK_SIZE) {
504 status = U_INTERNAL_PROGRAM_ERROR;
505 return &EmptyString;
506 }
507 ZNStringPoolChunk *oldChunk = fChunks;
508 fChunks = new ZNStringPoolChunk;
509 if (fChunks == NULL) {
510 status = U_MEMORY_ALLOCATION_ERROR;
511 return &EmptyString;
512 }
513 fChunks->fNext = oldChunk;
514 }
515
516 UChar *destString = &fChunks->fStrings[fChunks->fLimit];
517 u_strcpy(destString, s);
518 fChunks->fLimit += (length + 1);
519 uhash_put(fHash, destString, destString, &status);
520 return destString;
521 }
522
523
524 //
525 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
526 // into the pool's storage. Used for strings from resource bundles,
527 // which will perisist for the life of the zone string formatter, and
528 // therefore can be used directly without copying.
529 const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
530 const UChar *pooledString;
531 if (U_FAILURE(status)) {
532 return &EmptyString;
533 }
534 if (s != NULL) {
535 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
536 if (pooledString == NULL) {
537 UChar *ncs = const_cast<UChar *>(s);
538 uhash_put(fHash, ncs, ncs, &status);
539 }
540 }
541 return s;
542 }
543
544
545 const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
546 UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
547 return this->get(nonConstStr.getTerminatedBuffer(), status);
548 }
549
550 /*
551 * freeze(). Close the hash table that maps to the pooled strings.
552 * After freezing, the pool can not be searched or added to,
553 * but all existing references to pooled strings remain valid.
554 *
555 * The main purpose is to recover the storage used for the hash.
556 */
557 void ZNStringPool::freeze() {
558 uhash_close(fHash);
559 fHash = NULL;
560 }
561
562
563 /**
564 * This class stores name data for a meta zone or time zone.
565 */
566 class ZNames : public UMemory {
567 private:
568 friend class TimeZoneNamesImpl;
569
570 static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) {
571 switch(type) {
572 case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION;
573 case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC;
574 case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD;
575 case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT;
576 case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC;
577 case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD;
578 case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT;
579 default: return UTZNM_INDEX_UNKNOWN;
580 }
581 }
582 static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) {
583 switch(index) {
584 case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION;
585 case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC;
586 case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD;
587 case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT;
588 case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC;
589 case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD;
590 case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT;
591 default: return UTZNM_UNKNOWN;
592 }
593 }
594
595 const UChar* fNames[UTZNM_INDEX_COUNT];
596 UBool fDidAddIntoTrie;
597
598 // Whether we own the location string, if computed rather than loaded from a bundle.
599 // A meta zone names instance never has an exemplar location string.
600 UBool fOwnsLocationName;
601
602 ZNames(const UChar* names[], const UChar* locationName)
603 : fDidAddIntoTrie(FALSE) {
604 uprv_memcpy(fNames, names, sizeof(fNames));
605 if (locationName != NULL) {
606 fOwnsLocationName = TRUE;
607 fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName;
608 } else {
609 fOwnsLocationName = FALSE;
610 }
611 }
612
613 public:
614 ~ZNames() {
615 if (fOwnsLocationName) {
616 const UChar* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION];
617 U_ASSERT(locationName != NULL);
618 uprv_free((void*) locationName);
619 }
620 }
621
622 private:
623 static void* createMetaZoneAndPutInCache(UHashtable* cache, const UChar* names[],
624 const UnicodeString& mzID, UErrorCode& status) {
625 if (U_FAILURE(status)) { return NULL; }
626 U_ASSERT(names != NULL);
627
628 // Use the persistent ID as the resource key, so we can
629 // avoid duplications.
630 // TODO: Is there a more efficient way, like intern() in Java?
631 void* key = (void*) ZoneMeta::findMetaZoneID(mzID);
632 void* value;
633 if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
634 value = (void*) EMPTY;
635 } else {
636 value = (void*) (new ZNames(names, NULL));
637 if (value == NULL) {
638 status = U_MEMORY_ALLOCATION_ERROR;
639 return NULL;
640 }
641 }
642 uhash_put(cache, key, value, &status);
643 return value;
644 }
645
646 static void* createTimeZoneAndPutInCache(UHashtable* cache, const UChar* names[],
647 const UnicodeString& tzID, UErrorCode& status) {
648 if (U_FAILURE(status)) { return NULL; }
649 U_ASSERT(names != NULL);
650
651 // If necessary, compute the location name from the time zone name.
652 UChar* locationName = NULL;
653 if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == NULL) {
654 UnicodeString locationNameUniStr;
655 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
656
657 // Copy the computed location name to the heap
658 if (locationNameUniStr.length() > 0) {
659 const UChar* buff = locationNameUniStr.getTerminatedBuffer();
660 int32_t len = sizeof(UChar) * (locationNameUniStr.length() + 1);
661 locationName = (UChar*) uprv_malloc(len);
662 if (locationName == NULL) {
663 status = U_MEMORY_ALLOCATION_ERROR;
664 return NULL;
665 }
666 uprv_memcpy(locationName, buff, len);
667 }
668 }
669
670 // Use the persistent ID as the resource key, so we can
671 // avoid duplications.
672 // TODO: Is there a more efficient way, like intern() in Java?
673 void* key = (void*) ZoneMeta::findTimeZoneID(tzID);
674 void* value = (void*) (new ZNames(names, locationName));
675 if (value == NULL) {
676 status = U_MEMORY_ALLOCATION_ERROR;
677 return NULL;
678 }
679 uhash_put(cache, key, value, &status);
680 return value;
681 }
682
683 const UChar* getName(UTimeZoneNameType type) const {
684 UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
685 return index >= 0 ? fNames[index] : NULL;
686 }
687
688 void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
689 addNamesIntoTrie(mzID, NULL, trie, status);
690 }
691 void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) {
692 addNamesIntoTrie(NULL, tzID, trie, status);
693 }
694
695 void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie,
696 UErrorCode& status) {
697 if (U_FAILURE(status)) { return; }
698 if (fDidAddIntoTrie) { return; }
699 fDidAddIntoTrie = TRUE;
700
701 for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) {
702 const UChar* name = fNames[i];
703 if (name != NULL) {
704 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
705 if (nameinfo == NULL) {
706 status = U_MEMORY_ALLOCATION_ERROR;
707 return;
708 }
709 nameinfo->mzID = mzID;
710 nameinfo->tzID = tzID;
711 nameinfo->type = getTZNameType((UTimeZoneNameTypeIndex)i);
712 trie.put(name, nameinfo, status); // trie.put() takes ownership of the key
713 if (U_FAILURE(status)) {
714 return;
715 }
716 }
717 }
718 }
719
720 public:
721 struct ZNamesLoader;
722 };
723
724 struct ZNames::ZNamesLoader : public ResourceSink {
725 const UChar *names[UTZNM_INDEX_COUNT];
726
727 ZNamesLoader() {
728 clear();
729 }
730 virtual ~ZNamesLoader();
731
732 /** Reset for loading another set of names. */
733 void clear() {
734 uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
735 }
736
737 void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) {
738 if (U_FAILURE(errorCode)) { return; }
739
740 char key[ZID_KEY_MAX + 1];
741 mergeTimeZoneKey(mzID, key);
742
743 loadNames(zoneStrings, key, errorCode);
744 }
745
746 void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) {
747 // Replace "/" with ":".
748 UnicodeString uKey(tzID);
749 for (int32_t i = 0; i < uKey.length(); i++) {
750 if (uKey.charAt(i) == (UChar)0x2F) {
751 uKey.setCharAt(i, (UChar)0x3A);
752 }
753 }
754
755 char key[ZID_KEY_MAX + 1];
756 uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
757
758 loadNames(zoneStrings, key, errorCode);
759 }
760
761 void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
762 U_ASSERT(zoneStrings != NULL);
763 U_ASSERT(key != NULL);
764 U_ASSERT(key[0] != '\0');
765
766 UErrorCode localStatus = U_ZERO_ERROR;
767 clear();
768 ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
769
770 // Ignore errors, but propogate possible warnings.
771 if (U_SUCCESS(localStatus)) {
772 errorCode = localStatus;
773 }
774 }
775
776 void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
777 UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
778 if (type == UTZNM_INDEX_UNKNOWN) { return; }
779 if (names[type] == NULL) {
780 int32_t length;
781 // 'NO_NAME' indicates internally that this field should remain empty. It will be
782 // replaced by 'NULL' in getNames()
783 names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode);
784 }
785 }
786
787 virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
788 UErrorCode &errorCode) {
789 ResourceTable namesTable = value.getTable(errorCode);
790 if (U_FAILURE(errorCode)) { return; }
791 for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
792 if (value.isNoInheritanceMarker()) {
793 setNameIfEmpty(key, NULL, errorCode);
794 } else {
795 setNameIfEmpty(key, &value, errorCode);
796 }
797 }
798 }
799
800 static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
801 char c0, c1;
802 if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
803 return UTZNM_INDEX_UNKNOWN;
804 }
805 if (c0 == 'l') {
806 return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
807 c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
808 c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
809 } else if (c0 == 's') {
810 return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
811 c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
812 c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
813 } else if (c0 == 'e' && c1 == 'c') {
814 return UTZNM_INDEX_EXEMPLAR_LOCATION;
815 }
816 return UTZNM_INDEX_UNKNOWN;
817 }
818
819 /**
820 * Returns an array of names. It is the caller's responsibility to copy the data into a
821 * permanent location, as the returned array is owned by the loader instance and may be
822 * cleared or leave scope.
823 *
824 * This is different than Java, where the array will no longer be modified and null
825 * may be returned.
826 */
827 const UChar** getNames() {
828 // Remove 'NO_NAME' references in the array and replace with 'NULL'
829 for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
830 if (names[i] == NO_NAME) {
831 names[i] = NULL;
832 }
833 }
834 return names;
835 }
836 };
837
838 ZNames::ZNamesLoader::~ZNamesLoader() {}
839
840
841 // ---------------------------------------------------
842 // The meta zone ID enumeration class
843 // ---------------------------------------------------
844 class MetaZoneIDsEnumeration : public StringEnumeration {
845 public:
846 MetaZoneIDsEnumeration();
847 MetaZoneIDsEnumeration(const UVector& mzIDs);
848 MetaZoneIDsEnumeration(UVector* mzIDs);
849 virtual ~MetaZoneIDsEnumeration();
850 static UClassID U_EXPORT2 getStaticClassID(void);
851 virtual UClassID getDynamicClassID(void) const;
852 virtual const UnicodeString* snext(UErrorCode& status);
853 virtual void reset(UErrorCode& status);
854 virtual int32_t count(UErrorCode& status) const;
855 private:
856 int32_t fLen;
857 int32_t fPos;
858 const UVector* fMetaZoneIDs;
859 UVector *fLocalVector;
860 };
861
862 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
863
864 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
865 : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
866 }
867
868 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
869 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
870 fLen = fMetaZoneIDs->size();
871 }
872
873 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
874 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
875 if (fMetaZoneIDs) {
876 fLen = fMetaZoneIDs->size();
877 }
878 }
879
880 const UnicodeString*
881 MetaZoneIDsEnumeration::snext(UErrorCode& status) {
882 if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
883 unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
884 return &unistr;
885 }
886 return NULL;
887 }
888
889 void
890 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
891 fPos = 0;
892 }
893
894 int32_t
895 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
896 return fLen;
897 }
898
899 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
900 if (fLocalVector) {
901 delete fLocalVector;
902 }
903 }
904
905
906 // ---------------------------------------------------
907 // ZNameSearchHandler
908 // ---------------------------------------------------
909 class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
910 public:
911 ZNameSearchHandler(uint32_t types);
912 virtual ~ZNameSearchHandler();
913
914 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
915 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
916
917 private:
918 uint32_t fTypes;
919 int32_t fMaxMatchLen;
920 TimeZoneNames::MatchInfoCollection* fResults;
921 };
922
923 ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
924 : fTypes(types), fMaxMatchLen(0), fResults(NULL) {
925 }
926
927 ZNameSearchHandler::~ZNameSearchHandler() {
928 if (fResults != NULL) {
929 delete fResults;
930 }
931 }
932
933 UBool
934 ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
935 if (U_FAILURE(status)) {
936 return FALSE;
937 }
938 if (node->hasValues()) {
939 int32_t valuesCount = node->countValues();
940 for (int32_t i = 0; i < valuesCount; i++) {
941 ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
942 if (nameinfo == NULL) {
943 continue;
944 }
945 if ((nameinfo->type & fTypes) != 0) {
946 // matches a requested type
947 if (fResults == NULL) {
948 fResults = new TimeZoneNames::MatchInfoCollection();
949 if (fResults == NULL) {
950 status = U_MEMORY_ALLOCATION_ERROR;
951 }
952 }
953 if (U_SUCCESS(status)) {
954 U_ASSERT(fResults != NULL);
955 if (nameinfo->tzID) {
956 fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
957 } else {
958 U_ASSERT(nameinfo->mzID);
959 fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
960 }
961 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
962 fMaxMatchLen = matchLength;
963 }
964 }
965 }
966 }
967 }
968 return TRUE;
969 }
970
971 TimeZoneNames::MatchInfoCollection*
972 ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
973 // give the ownership to the caller
974 TimeZoneNames::MatchInfoCollection* results = fResults;
975 maxMatchLen = fMaxMatchLen;
976
977 // reset
978 fResults = NULL;
979 fMaxMatchLen = 0;
980 return results;
981 }
982
983 // ---------------------------------------------------
984 // TimeZoneNamesImpl
985 //
986 // TimeZoneNames implementation class. This is the main
987 // part of this module.
988 // ---------------------------------------------------
989
990 U_CDECL_BEGIN
991 /**
992 * Deleter for ZNames
993 */
994 static void U_CALLCONV
995 deleteZNames(void *obj) {
996 if (obj != EMPTY) {
997 delete (ZNames*) obj;
998 }
999 }
1000
1001 /**
1002 * Deleter for ZNameInfo
1003 */
1004 static void U_CALLCONV
1005 deleteZNameInfo(void *obj) {
1006 uprv_free(obj);
1007 }
1008
1009 U_CDECL_END
1010
1011 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
1012 : fLocale(locale),
1013 fZoneStrings(NULL),
1014 fTZNamesMap(NULL),
1015 fMZNamesMap(NULL),
1016 fNamesTrieFullyLoaded(FALSE),
1017 fNamesFullyLoaded(FALSE),
1018 fNamesTrie(TRUE, deleteZNameInfo) {
1019 initialize(locale, status);
1020 }
1021
1022 void
1023 TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
1024 if (U_FAILURE(status)) {
1025 return;
1026 }
1027
1028 // Load zoneStrings bundle
1029 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
1030 fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
1031 fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
1032 if (U_FAILURE(tmpsts)) {
1033 status = tmpsts;
1034 cleanup();
1035 return;
1036 }
1037
1038 // Initialize hashtables holding time zone/meta zone names
1039 fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1040 fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1041 if (U_FAILURE(status)) {
1042 cleanup();
1043 return;
1044 }
1045
1046 uhash_setValueDeleter(fMZNamesMap, deleteZNames);
1047 uhash_setValueDeleter(fTZNamesMap, deleteZNames);
1048 // no key deleters for name maps
1049
1050 // preload zone strings for the default zone
1051 TimeZone *tz = TimeZone::createDefault();
1052 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
1053 if (tzID != NULL) {
1054 loadStrings(UnicodeString(tzID), status);
1055 }
1056 delete tz;
1057
1058 return;
1059 }
1060
1061 /*
1062 * This method updates the cache and must be called with a lock,
1063 * except initializer.
1064 */
1065 void
1066 TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
1067 loadTimeZoneNames(tzCanonicalID, status);
1068 LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
1069 if (U_FAILURE(status)) { return; }
1070 U_ASSERT(!mzIDs.isNull());
1071
1072 const UnicodeString *mzID;
1073 while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
1074 loadMetaZoneNames(*mzID, status);
1075 }
1076 }
1077
1078 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1079 cleanup();
1080 }
1081
1082 void
1083 TimeZoneNamesImpl::cleanup() {
1084 if (fZoneStrings != NULL) {
1085 ures_close(fZoneStrings);
1086 fZoneStrings = NULL;
1087 }
1088 if (fMZNamesMap != NULL) {
1089 uhash_close(fMZNamesMap);
1090 fMZNamesMap = NULL;
1091 }
1092 if (fTZNamesMap != NULL) {
1093 uhash_close(fTZNamesMap);
1094 fTZNamesMap = NULL;
1095 }
1096 }
1097
1098 UBool
1099 TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
1100 if (this == &other) {
1101 return TRUE;
1102 }
1103 // No implementation for now
1104 return FALSE;
1105 }
1106
1107 TimeZoneNames*
1108 TimeZoneNamesImpl::clone() const {
1109 UErrorCode status = U_ZERO_ERROR;
1110 return new TimeZoneNamesImpl(fLocale, status);
1111 }
1112
1113 StringEnumeration*
1114 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
1115 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
1116 }
1117
1118 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1119 StringEnumeration*
1120 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
1121 if (U_FAILURE(status)) {
1122 return NULL;
1123 }
1124 const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
1125 if (mzIDs == NULL) {
1126 return new MetaZoneIDsEnumeration();
1127 }
1128 return new MetaZoneIDsEnumeration(*mzIDs);
1129 }
1130
1131 StringEnumeration*
1132 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
1133 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
1134 }
1135
1136 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1137 StringEnumeration*
1138 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
1139 if (U_FAILURE(status)) {
1140 return NULL;
1141 }
1142 const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1143 if (mappings == NULL) {
1144 return new MetaZoneIDsEnumeration();
1145 }
1146
1147 MetaZoneIDsEnumeration *senum = NULL;
1148 UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
1149 if (mzIDs == NULL) {
1150 status = U_MEMORY_ALLOCATION_ERROR;
1151 }
1152 if (U_SUCCESS(status)) {
1153 U_ASSERT(mzIDs != NULL);
1154 for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1155
1156 OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
1157 const UChar *mzID = map->mzid;
1158 if (!mzIDs->contains((void *)mzID)) {
1159 mzIDs->addElement((void *)mzID, status);
1160 }
1161 }
1162 if (U_SUCCESS(status)) {
1163 senum = new MetaZoneIDsEnumeration(mzIDs);
1164 } else {
1165 delete mzIDs;
1166 }
1167 }
1168 return senum;
1169 }
1170
1171 UnicodeString&
1172 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1173 return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
1174 }
1175
1176 // static implementation of getMetaZoneID
1177 UnicodeString&
1178 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
1179 ZoneMeta::getMetazoneID(tzID, date, mzID);
1180 return mzID;
1181 }
1182
1183 UnicodeString&
1184 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1185 return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
1186 }
1187
1188 // static implementaion of getReferenceZoneID
1189 UnicodeString&
1190 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
1191 ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1192 return tzID;
1193 }
1194
1195 UnicodeString&
1196 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1197 UTimeZoneNameType type,
1198 UnicodeString& name) const {
1199 name.setToBogus(); // cleanup result.
1200 if (mzID.isEmpty()) {
1201 return name;
1202 }
1203
1204 ZNames *znames = NULL;
1205 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1206
1207 {
1208 Mutex lock(&gDataMutex);
1209 UErrorCode status = U_ZERO_ERROR;
1210 znames = nonConstThis->loadMetaZoneNames(mzID, status);
1211 if (U_FAILURE(status)) { return name; }
1212 }
1213
1214 if (znames != NULL) {
1215 const UChar* s = znames->getName(type);
1216 if (s != NULL) {
1217 name.setTo(TRUE, s, -1);
1218 }
1219 }
1220 return name;
1221 }
1222
1223 UnicodeString&
1224 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1225 name.setToBogus(); // cleanup result.
1226 if (tzID.isEmpty()) {
1227 return name;
1228 }
1229
1230 ZNames *tznames = NULL;
1231 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1232
1233 {
1234 Mutex lock(&gDataMutex);
1235 UErrorCode status = U_ZERO_ERROR;
1236 tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1237 if (U_FAILURE(status)) { return name; }
1238 }
1239
1240 if (tznames != NULL) {
1241 const UChar *s = tznames->getName(type);
1242 if (s != NULL) {
1243 name.setTo(TRUE, s, -1);
1244 }
1245 }
1246 return name;
1247 }
1248
1249 UnicodeString&
1250 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
1251 name.setToBogus(); // cleanup result.
1252 const UChar* locName = NULL;
1253 ZNames *tznames = NULL;
1254 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1255
1256 {
1257 Mutex lock(&gDataMutex);
1258 UErrorCode status = U_ZERO_ERROR;
1259 tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1260 if (U_FAILURE(status)) { return name; }
1261 }
1262
1263 if (tznames != NULL) {
1264 locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
1265 }
1266 if (locName != NULL) {
1267 name.setTo(TRUE, locName, -1);
1268 }
1269
1270 return name;
1271 }
1272
1273
1274 // Merge the MZ_PREFIX and mzId
1275 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
1276 if (mzID.isEmpty()) {
1277 result[0] = '\0';
1278 return;
1279 }
1280
1281 char mzIdChar[ZID_KEY_MAX + 1];
1282 int32_t keyLen;
1283 int32_t prefixLen = uprv_strlen(gMZPrefix);
1284 keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
1285 uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
1286 uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
1287 result[keyLen + prefixLen] = '\0';
1288 }
1289
1290 /*
1291 * This method updates the cache and must be called with a lock
1292 */
1293 ZNames*
1294 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
1295 if (U_FAILURE(status)) { return NULL; }
1296 U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
1297
1298 UChar mzIDKey[ZID_KEY_MAX + 1];
1299 mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
1300 U_ASSERT(U_SUCCESS(status)); // already checked length above
1301 mzIDKey[mzID.length()] = 0;
1302
1303 void* mznames = uhash_get(fMZNamesMap, mzIDKey);
1304 if (mznames == NULL) {
1305 ZNames::ZNamesLoader loader;
1306 loader.loadMetaZone(fZoneStrings, mzID, status);
1307 mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
1308 if (U_FAILURE(status)) { return NULL; }
1309 }
1310
1311 if (mznames != EMPTY) {
1312 return (ZNames*)mznames;
1313 } else {
1314 return NULL;
1315 }
1316 }
1317
1318 /*
1319 * This method updates the cache and must be called with a lock
1320 */
1321 ZNames*
1322 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
1323 if (U_FAILURE(status)) { return NULL; }
1324 U_ASSERT(tzID.length() <= ZID_KEY_MAX);
1325
1326 UChar tzIDKey[ZID_KEY_MAX + 1];
1327 int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
1328 U_ASSERT(U_SUCCESS(status)); // already checked length above
1329 tzIDKey[tzIDKeyLen] = 0;
1330
1331 void *tznames = uhash_get(fTZNamesMap, tzIDKey);
1332 if (tznames == NULL) {
1333 ZNames::ZNamesLoader loader;
1334 loader.loadTimeZone(fZoneStrings, tzID, status);
1335 tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
1336 if (U_FAILURE(status)) { return NULL; }
1337 }
1338
1339 // tznames is never EMPTY
1340 return (ZNames*)tznames;
1341 }
1342
1343 TimeZoneNames::MatchInfoCollection*
1344 TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1345 ZNameSearchHandler handler(types);
1346 TimeZoneNames::MatchInfoCollection* matches;
1347 TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1348
1349 // Synchronize so that data is not loaded multiple times.
1350 // TODO: Consider more fine-grained synchronization.
1351 {
1352 Mutex lock(&gDataMutex);
1353
1354 // First try of lookup.
1355 matches = doFind(handler, text, start, status);
1356 if (U_FAILURE(status)) { return NULL; }
1357 if (matches != NULL) {
1358 return matches;
1359 }
1360
1361 // All names are not yet loaded into the trie.
1362 // We may have loaded names for formatting several time zones,
1363 // and might be parsing one of those.
1364 // Populate the parsing trie from all of the already-loaded names.
1365 nonConstThis->addAllNamesIntoTrie(status);
1366
1367 // Second try of lookup.
1368 matches = doFind(handler, text, start, status);
1369 if (U_FAILURE(status)) { return NULL; }
1370 if (matches != NULL) {
1371 return matches;
1372 }
1373
1374 // There are still some names we haven't loaded into the trie yet.
1375 // Load everything now.
1376 nonConstThis->internalLoadAllDisplayNames(status);
1377 nonConstThis->addAllNamesIntoTrie(status);
1378 nonConstThis->fNamesTrieFullyLoaded = TRUE;
1379 if (U_FAILURE(status)) { return NULL; }
1380
1381 // Third try: we must return this one.
1382 return doFind(handler, text, start, status);
1383 }
1384 }
1385
1386 TimeZoneNames::MatchInfoCollection*
1387 TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
1388 const UnicodeString& text, int32_t start, UErrorCode& status) const {
1389
1390 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1391 if (U_FAILURE(status)) { return NULL; }
1392
1393 int32_t maxLen = 0;
1394 TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
1395 if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1396 // perfect match, or no more names available
1397 return matches;
1398 }
1399 delete matches;
1400 return NULL;
1401 }
1402
1403 // Caller must synchronize.
1404 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
1405 if (U_FAILURE(status)) return;
1406 int32_t pos;
1407 const UHashElement* element;
1408
1409 pos = UHASH_FIRST;
1410 while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) {
1411 if (element->value.pointer == EMPTY) { continue; }
1412 UChar* mzID = (UChar*) element->key.pointer;
1413 ZNames* znames = (ZNames*) element->value.pointer;
1414 znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
1415 if (U_FAILURE(status)) { return; }
1416 }
1417
1418 pos = UHASH_FIRST;
1419 while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) {
1420 if (element->value.pointer == EMPTY) { continue; }
1421 UChar* tzID = (UChar*) element->key.pointer;
1422 ZNames* znames = (ZNames*) element->value.pointer;
1423 znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
1424 if (U_FAILURE(status)) { return; }
1425 }
1426 }
1427
1428 U_CDECL_BEGIN
1429 static void U_CALLCONV
1430 deleteZNamesLoader(void* obj) {
1431 if (obj == DUMMY_LOADER) { return; }
1432 const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
1433 delete loader;
1434 }
1435 U_CDECL_END
1436
1437 struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
1438 TimeZoneNamesImpl& tzn;
1439 UHashtable* keyToLoader;
1440
1441 ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
1442 : tzn(_tzn) {
1443 keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
1444 if (U_FAILURE(status)) { return; }
1445 uhash_setKeyDeleter(keyToLoader, uprv_free);
1446 uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
1447 }
1448 virtual ~ZoneStringsLoader();
1449
1450 void* createKey(const char* key, UErrorCode& status) {
1451 int32_t len = sizeof(char) * (uprv_strlen(key) + 1);
1452 char* newKey = (char*) uprv_malloc(len);
1453 if (newKey == NULL) {
1454 status = U_MEMORY_ALLOCATION_ERROR;
1455 return NULL;
1456 }
1457 uprv_memcpy(newKey, key, len);
1458 newKey[len-1] = '\0';
1459 return (void*) newKey;
1460 }
1461
1462 UBool isMetaZone(const char* key) {
1463 return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
1464 }
1465
1466 UnicodeString mzIDFromKey(const char* key) {
1467 return UnicodeString(key + MZ_PREFIX_LEN, uprv_strlen(key) - MZ_PREFIX_LEN, US_INV);
1468 }
1469
1470 UnicodeString tzIDFromKey(const char* key) {
1471 UnicodeString tzID(key, -1, US_INV);
1472 // Replace all colons ':' with slashes '/'
1473 for (int i=0; i<tzID.length(); i++) {
1474 if (tzID.charAt(i) == 0x003A) {
1475 tzID.setCharAt(i, 0x002F);
1476 }
1477 }
1478 return tzID;
1479 }
1480
1481 void load(UErrorCode& status) {
1482 ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
1483 if (U_FAILURE(status)) { return; }
1484
1485 int32_t pos = UHASH_FIRST;
1486 const UHashElement* element;
1487 while ((element = uhash_nextElement(keyToLoader, &pos)) != NULL) {
1488 if (element->value.pointer == DUMMY_LOADER) { continue; }
1489 ZNames::ZNamesLoader* loader = (ZNames::ZNamesLoader*) element->value.pointer;
1490 char* key = (char*) element->key.pointer;
1491
1492 if (isMetaZone(key)) {
1493 UnicodeString mzID = mzIDFromKey(key);
1494 ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
1495 } else {
1496 UnicodeString tzID = tzIDFromKey(key);
1497 ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
1498 }
1499 if (U_FAILURE(status)) { return; }
1500 }
1501 }
1502
1503 void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
1504 UErrorCode &status) {
1505 if (U_FAILURE(status)) { return; }
1506
1507 void* loader = uhash_get(keyToLoader, key);
1508 if (loader == NULL) {
1509 if (isMetaZone(key)) {
1510 UnicodeString mzID = mzIDFromKey(key);
1511 void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
1512 if (cacheVal != NULL) {
1513 // We have already loaded the names for this meta zone.
1514 loader = (void*) DUMMY_LOADER;
1515 } else {
1516 loader = (void*) new ZNames::ZNamesLoader();
1517 if (loader == NULL) {
1518 status = U_MEMORY_ALLOCATION_ERROR;
1519 return;
1520 }
1521 }
1522 } else {
1523 UnicodeString tzID = tzIDFromKey(key);
1524 void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
1525 if (cacheVal != NULL) {
1526 // We have already loaded the names for this time zone.
1527 loader = (void*) DUMMY_LOADER;
1528 } else {
1529 loader = (void*) new ZNames::ZNamesLoader();
1530 if (loader == NULL) {
1531 status = U_MEMORY_ALLOCATION_ERROR;
1532 return;
1533 }
1534 }
1535 }
1536
1537 void* newKey = createKey(key, status);
1538 if (U_FAILURE(status)) {
1539 deleteZNamesLoader(loader);
1540 return;
1541 }
1542
1543 uhash_put(keyToLoader, newKey, loader, &status);
1544 if (U_FAILURE(status)) { return; }
1545 }
1546
1547 if (loader != DUMMY_LOADER) {
1548 // Let the ZNamesLoader consume the names table.
1549 ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status);
1550 }
1551 }
1552
1553 virtual void put(const char *key, ResourceValue &value, UBool noFallback,
1554 UErrorCode &status) {
1555 ResourceTable timeZonesTable = value.getTable(status);
1556 if (U_FAILURE(status)) { return; }
1557 for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
1558 U_ASSERT(!value.isNoInheritanceMarker());
1559 if (value.getType() == URES_TABLE) {
1560 consumeNamesTable(key, value, noFallback, status);
1561 } else {
1562 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1563 // All time zone fields are tables.
1564 }
1565 if (U_FAILURE(status)) { return; }
1566 }
1567 }
1568 };
1569
1570 // Virtual destructors must be defined out of line.
1571 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1572 uhash_close(keyToLoader);
1573 }
1574
1575 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
1576 if (U_FAILURE(status)) return;
1577
1578 {
1579 Mutex lock(&gDataMutex);
1580 internalLoadAllDisplayNames(status);
1581 }
1582 }
1583
1584 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
1585 const UTimeZoneNameType types[], int32_t numTypes,
1586 UDate date, UnicodeString dest[], UErrorCode& status) const {
1587 if (U_FAILURE(status)) return;
1588
1589 if (tzID.isEmpty()) { return; }
1590 void* tznames = NULL;
1591 void* mznames = NULL;
1592 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1593
1594 // Load the time zone strings
1595 {
1596 Mutex lock(&gDataMutex);
1597 tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
1598 if (U_FAILURE(status)) { return; }
1599 }
1600 U_ASSERT(tznames != NULL);
1601
1602 // Load the values into the dest array
1603 for (int i = 0; i < numTypes; i++) {
1604 UTimeZoneNameType type = types[i];
1605 const UChar* name = ((ZNames*)tznames)->getName(type);
1606 if (name == NULL) {
1607 if (mznames == NULL) {
1608 // Load the meta zone name
1609 UnicodeString mzID;
1610 getMetaZoneID(tzID, date, mzID);
1611 if (mzID.isEmpty()) {
1612 mznames = (void*) EMPTY;
1613 } else {
1614 // Load the meta zone strings
1615 // Mutex is scoped to the "else" statement
1616 Mutex lock(&gDataMutex);
1617 mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
1618 if (U_FAILURE(status)) { return; }
1619 // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1620 // a dummy object instead of NULL.
1621 if (mznames == NULL) {
1622 mznames = (void*) EMPTY;
1623 }
1624 }
1625 }
1626 U_ASSERT(mznames != NULL);
1627 if (mznames != EMPTY) {
1628 name = ((ZNames*)mznames)->getName(type);
1629 }
1630 }
1631 if (name != NULL) {
1632 dest[i].setTo(TRUE, name, -1);
1633 } else {
1634 dest[i].setToBogus();
1635 }
1636 }
1637 }
1638
1639 // Caller must synchronize.
1640 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
1641 if (!fNamesFullyLoaded) {
1642 fNamesFullyLoaded = TRUE;
1643
1644 ZoneStringsLoader loader(*this, status);
1645 loader.load(status);
1646 if (U_FAILURE(status)) { return; }
1647
1648 const UnicodeString *id;
1649
1650 // load strings for all zones
1651 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
1652 UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1653 if (U_SUCCESS(status)) {
1654 while ((id = tzIDs->snext(status))) {
1655 if (U_FAILURE(status)) {
1656 break;
1657 }
1658 UnicodeString copy(*id);
1659 void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
1660 if (value == NULL) {
1661 // loadStrings also loads related metazone strings
1662 loadStrings(*id, status);
1663 }
1664 }
1665 }
1666 if (tzIDs != NULL) {
1667 delete tzIDs;
1668 }
1669 }
1670 }
1671
1672
1673
1674 static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1675 static const int32_t gEtcPrefixLen = 4;
1676 static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1677 static const int32_t gSystemVPrefixLen = 8;
1678 static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1679 static const int32_t gRiyadh8Len = 7;
1680
1681 UnicodeString& U_EXPORT2
1682 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
1683 if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
1684 || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
1685 name.setToBogus();
1686 return name;
1687 }
1688
1689 int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
1690 if (sep > 0 && sep + 1 < tzID.length()) {
1691 name.setTo(tzID, sep + 1);
1692 name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
1693 UnicodeString((UChar)0x20 /* space */));
1694 } else {
1695 name.setToBogus();
1696 }
1697 return name;
1698 }
1699
1700 // ---------------------------------------------------
1701 // TZDBTimeZoneNames and its supporting classes
1702 //
1703 // TZDBTimeZoneNames is an implementation class of
1704 // TimeZoneNames holding the IANA tz database abbreviations.
1705 // ---------------------------------------------------
1706
1707 class TZDBNames : public UMemory {
1708 public:
1709 virtual ~TZDBNames();
1710
1711 static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
1712 const UChar* getName(UTimeZoneNameType type) const;
1713 const char** getParseRegions(int32_t& numRegions) const;
1714
1715 protected:
1716 TZDBNames(const UChar** names, char** regions, int32_t numRegions);
1717
1718 private:
1719 const UChar** fNames;
1720 char** fRegions;
1721 int32_t fNumRegions;
1722 };
1723
1724 TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions)
1725 : fNames(names),
1726 fRegions(regions),
1727 fNumRegions(numRegions) {
1728 }
1729
1730 TZDBNames::~TZDBNames() {
1731 if (fNames != NULL) {
1732 uprv_free(fNames);
1733 }
1734 if (fRegions != NULL) {
1735 char **p = fRegions;
1736 for (int32_t i = 0; i < fNumRegions; p++, i++) {
1737 uprv_free(*p);
1738 }
1739 uprv_free(fRegions);
1740 }
1741 }
1742
1743 TZDBNames*
1744 TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
1745 if (rb == NULL || key == NULL || *key == 0) {
1746 return NULL;
1747 }
1748
1749 UErrorCode status = U_ZERO_ERROR;
1750
1751 const UChar **names = NULL;
1752 char** regions = NULL;
1753 int32_t numRegions = 0;
1754
1755 int32_t len = 0;
1756
1757 UResourceBundle* rbTable = NULL;
1758 rbTable = ures_getByKey(rb, key, rbTable, &status);
1759 if (U_FAILURE(status)) {
1760 return NULL;
1761 }
1762
1763 names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE);
1764 UBool isEmpty = TRUE;
1765 if (names != NULL) {
1766 for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
1767 status = U_ZERO_ERROR;
1768 const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
1769 if (U_FAILURE(status) || len == 0) {
1770 names[i] = NULL;
1771 } else {
1772 names[i] = value;
1773 isEmpty = FALSE;
1774 }
1775 }
1776 }
1777
1778 if (isEmpty) {
1779 if (names != NULL) {
1780 uprv_free(names);
1781 }
1782 return NULL;
1783 }
1784
1785 UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status);
1786 UBool regionError = FALSE;
1787 if (U_SUCCESS(status)) {
1788 numRegions = ures_getSize(regionsRes);
1789 if (numRegions > 0) {
1790 regions = (char**)uprv_malloc(sizeof(char*) * numRegions);
1791 if (regions != NULL) {
1792 char **pRegion = regions;
1793 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1794 *pRegion = NULL;
1795 }
1796 // filling regions
1797 pRegion = regions;
1798 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1799 status = U_ZERO_ERROR;
1800 const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
1801 if (U_FAILURE(status)) {
1802 regionError = TRUE;
1803 break;
1804 }
1805 *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1));
1806 if (*pRegion == NULL) {
1807 regionError = TRUE;
1808 break;
1809 }
1810 u_UCharsToChars(uregion, *pRegion, len);
1811 (*pRegion)[len] = 0;
1812 }
1813 }
1814 }
1815 }
1816 ures_close(regionsRes);
1817 ures_close(rbTable);
1818
1819 if (regionError) {
1820 if (names != NULL) {
1821 uprv_free(names);
1822 }
1823 if (regions != NULL) {
1824 char **p = regions;
1825 for (int32_t i = 0; i < numRegions; p++, i++) {
1826 uprv_free(*p);
1827 }
1828 uprv_free(regions);
1829 }
1830 return NULL;
1831 }
1832
1833 return new TZDBNames(names, regions, numRegions);
1834 }
1835
1836 const UChar*
1837 TZDBNames::getName(UTimeZoneNameType type) const {
1838 if (fNames == NULL) {
1839 return NULL;
1840 }
1841 const UChar *name = NULL;
1842 switch(type) {
1843 case UTZNM_SHORT_STANDARD:
1844 name = fNames[0];
1845 break;
1846 case UTZNM_SHORT_DAYLIGHT:
1847 name = fNames[1];
1848 break;
1849 default:
1850 name = NULL;
1851 }
1852 return name;
1853 }
1854
1855 const char**
1856 TZDBNames::getParseRegions(int32_t& numRegions) const {
1857 if (fRegions == NULL) {
1858 numRegions = 0;
1859 } else {
1860 numRegions = fNumRegions;
1861 }
1862 return (const char**)fRegions;
1863 }
1864
1865 U_CDECL_BEGIN
1866 /**
1867 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1868 * in the trie
1869 */
1870 typedef struct TZDBNameInfo {
1871 const UChar* mzID;
1872 UTimeZoneNameType type;
1873 UBool ambiguousType;
1874 const char** parseRegions;
1875 int32_t nRegions;
1876 } TZDBNameInfo;
1877 U_CDECL_END
1878
1879
1880 class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
1881 public:
1882 TZDBNameSearchHandler(uint32_t types, const char* region);
1883 virtual ~TZDBNameSearchHandler();
1884
1885 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
1886 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
1887
1888 private:
1889 uint32_t fTypes;
1890 int32_t fMaxMatchLen;
1891 TimeZoneNames::MatchInfoCollection* fResults;
1892 const char* fRegion;
1893 };
1894
1895 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region)
1896 : fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) {
1897 }
1898
1899 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1900 if (fResults != NULL) {
1901 delete fResults;
1902 }
1903 }
1904
1905 UBool
1906 TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
1907 if (U_FAILURE(status)) {
1908 return FALSE;
1909 }
1910
1911 TZDBNameInfo *match = NULL;
1912 TZDBNameInfo *defaultRegionMatch = NULL;
1913
1914 if (node->hasValues()) {
1915 int32_t valuesCount = node->countValues();
1916 for (int32_t i = 0; i < valuesCount; i++) {
1917 TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
1918 if (ninfo == NULL) {
1919 continue;
1920 }
1921 if ((ninfo->type & fTypes) != 0) {
1922 // Some tz database abbreviations are ambiguous. For example,
1923 // CST means either Central Standard Time or China Standard Time.
1924 // Unlike CLDR time zone display names, this implementation
1925 // does not use unique names. And TimeZoneFormat does not expect
1926 // multiple results returned for the same time zone type.
1927 // For this reason, this implementation resolve one among same
1928 // zone type with a same name at this level.
1929 if (ninfo->parseRegions == NULL) {
1930 // parseRegions == null means this is the default metazone
1931 // mapping for the abbreviation.
1932 if (defaultRegionMatch == NULL) {
1933 match = defaultRegionMatch = ninfo;
1934 }
1935 } else {
1936 UBool matchRegion = FALSE;
1937 // non-default metazone mapping for an abbreviation
1938 // comes with applicable regions. For example, the default
1939 // metazone mapping for "CST" is America_Central,
1940 // but if region is one of CN/MO/TW, "CST" is parsed
1941 // as metazone China (China Standard Time).
1942 for (int32_t i = 0; i < ninfo->nRegions; i++) {
1943 const char *region = ninfo->parseRegions[i];
1944 if (uprv_strcmp(fRegion, region) == 0) {
1945 match = ninfo;
1946 matchRegion = TRUE;
1947 break;
1948 }
1949 }
1950 if (matchRegion) {
1951 break;
1952 }
1953 if (match == NULL) {
1954 match = ninfo;
1955 }
1956 }
1957 }
1958 }
1959
1960 if (match != NULL) {
1961 UTimeZoneNameType ntype = match->type;
1962 // Note: Workaround for duplicated standard/daylight names
1963 // The tz database contains a few zones sharing a
1964 // same name for both standard time and daylight saving
1965 // time. For example, Australia/Sydney observes DST,
1966 // but "EST" is used for both standard and daylight.
1967 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1968 // in the find operation, we cannot tell which one was
1969 // actually matched.
1970 // TimeZoneFormat#parse returns a matched name type (standard
1971 // or daylight) and DateFormat implementation uses the info to
1972 // to adjust actual time. To avoid false type information,
1973 // this implementation replaces the name type with SHORT_GENERIC.
1974 if (match->ambiguousType
1975 && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
1976 && (fTypes & UTZNM_SHORT_STANDARD) != 0
1977 && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
1978 ntype = UTZNM_SHORT_GENERIC;
1979 }
1980
1981 if (fResults == NULL) {
1982 fResults = new TimeZoneNames::MatchInfoCollection();
1983 if (fResults == NULL) {
1984 status = U_MEMORY_ALLOCATION_ERROR;
1985 }
1986 }
1987 if (U_SUCCESS(status)) {
1988 U_ASSERT(fResults != NULL);
1989 U_ASSERT(match->mzID != NULL);
1990 fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
1991 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
1992 fMaxMatchLen = matchLength;
1993 }
1994 }
1995 }
1996 }
1997 return TRUE;
1998 }
1999
2000 TimeZoneNames::MatchInfoCollection*
2001 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
2002 // give the ownership to the caller
2003 TimeZoneNames::MatchInfoCollection* results = fResults;
2004 maxMatchLen = fMaxMatchLen;
2005
2006 // reset
2007 fResults = NULL;
2008 fMaxMatchLen = 0;
2009 return results;
2010 }
2011
2012 U_CDECL_BEGIN
2013 /**
2014 * Deleter for TZDBNames
2015 */
2016 static void U_CALLCONV
2017 deleteTZDBNames(void *obj) {
2018 if (obj != EMPTY) {
2019 delete (TZDBNames *)obj;
2020 }
2021 }
2022
2023 static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
2024 gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
2025 if (U_FAILURE(status)) {
2026 gTZDBNamesMap = NULL;
2027 return;
2028 }
2029 // no key deleters for tzdb name maps
2030 uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
2031 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2032 }
2033
2034 /**
2035 * Deleter for TZDBNameInfo
2036 */
2037 static void U_CALLCONV
2038 deleteTZDBNameInfo(void *obj) {
2039 if (obj != NULL) {
2040 uprv_free(obj);
2041 }
2042 }
2043
2044 static void U_CALLCONV prepareFind(UErrorCode &status) {
2045 if (U_FAILURE(status)) {
2046 return;
2047 }
2048 gTZDBNamesTrie = new TextTrieMap(TRUE, deleteTZDBNameInfo);
2049 if (gTZDBNamesTrie == NULL) {
2050 status = U_MEMORY_ALLOCATION_ERROR;
2051 return;
2052 }
2053
2054 const UnicodeString *mzID;
2055 StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2056 if (U_SUCCESS(status)) {
2057 while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
2058 const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
2059 if (names == NULL) {
2060 continue;
2061 }
2062 const UChar *std = names->getName(UTZNM_SHORT_STANDARD);
2063 const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
2064 if (std == NULL && dst == NULL) {
2065 continue;
2066 }
2067 int32_t numRegions = 0;
2068 const char **parseRegions = names->getParseRegions(numRegions);
2069
2070 // The tz database contains a few zones sharing a
2071 // same name for both standard time and daylight saving
2072 // time. For example, Australia/Sydney observes DST,
2073 // but "EST" is used for both standard and daylight.
2074 // we need to store the information for later processing.
2075 UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0);
2076
2077 const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID);
2078 if (std != NULL) {
2079 TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2080 if (stdInf == NULL) {
2081 status = U_MEMORY_ALLOCATION_ERROR;
2082 break;
2083 }
2084 stdInf->mzID = uMzID;
2085 stdInf->type = UTZNM_SHORT_STANDARD;
2086 stdInf->ambiguousType = ambiguousType;
2087 stdInf->parseRegions = parseRegions;
2088 stdInf->nRegions = numRegions;
2089 gTZDBNamesTrie->put(std, stdInf, status);
2090 }
2091 if (U_SUCCESS(status) && dst != NULL) {
2092 TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2093 if (dstInf == NULL) {
2094 status = U_MEMORY_ALLOCATION_ERROR;
2095 break;
2096 }
2097 dstInf->mzID = uMzID;
2098 dstInf->type = UTZNM_SHORT_DAYLIGHT;
2099 dstInf->ambiguousType = ambiguousType;
2100 dstInf->parseRegions = parseRegions;
2101 dstInf->nRegions = numRegions;
2102 gTZDBNamesTrie->put(dst, dstInf, status);
2103 }
2104 }
2105 }
2106 delete mzIDs;
2107
2108 if (U_FAILURE(status)) {
2109 delete gTZDBNamesTrie;
2110 gTZDBNamesTrie = NULL;
2111 return;
2112 }
2113
2114 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2115 }
2116
2117 U_CDECL_END
2118
2119 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
2120 : fLocale(locale) {
2121 UBool useWorld = TRUE;
2122 const char* region = fLocale.getCountry();
2123 int32_t regionLen = uprv_strlen(region);
2124 if (regionLen == 0) {
2125 UErrorCode status = U_ZERO_ERROR;
2126 char loc[ULOC_FULLNAME_CAPACITY];
2127 uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
2128 regionLen = uloc_getCountry(loc, fRegion, sizeof(fRegion), &status);
2129 if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) {
2130 useWorld = FALSE;
2131 }
2132 } else if (regionLen < (int32_t)sizeof(fRegion)) {
2133 uprv_strcpy(fRegion, region);
2134 useWorld = FALSE;
2135 }
2136 if (useWorld) {
2137 uprv_strcpy(fRegion, "001");
2138 }
2139 }
2140
2141 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2142 }
2143
2144 UBool
2145 TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
2146 if (this == &other) {
2147 return TRUE;
2148 }
2149 // No implementation for now
2150 return FALSE;
2151 }
2152
2153 TimeZoneNames*
2154 TZDBTimeZoneNames::clone() const {
2155 return new TZDBTimeZoneNames(fLocale);
2156 }
2157
2158 StringEnumeration*
2159 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
2160 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2161 }
2162
2163 StringEnumeration*
2164 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
2165 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
2166 }
2167
2168 UnicodeString&
2169 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
2170 return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
2171 }
2172
2173 UnicodeString&
2174 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
2175 return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
2176 }
2177
2178 UnicodeString&
2179 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
2180 UTimeZoneNameType type,
2181 UnicodeString& name) const {
2182 name.setToBogus();
2183 if (mzID.isEmpty()) {
2184 return name;
2185 }
2186
2187 UErrorCode status = U_ZERO_ERROR;
2188 const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
2189 if (U_SUCCESS(status)) {
2190 const UChar *s = tzdbNames->getName(type);
2191 if (s != NULL) {
2192 name.setTo(TRUE, s, -1);
2193 }
2194 }
2195
2196 return name;
2197 }
2198
2199 UnicodeString&
2200 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
2201 // No abbreviations associated a zone directly for now.
2202 name.setToBogus();
2203 return name;
2204 }
2205
2206 TZDBTimeZoneNames::MatchInfoCollection*
2207 TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
2208 umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
2209 if (U_FAILURE(status)) {
2210 return NULL;
2211 }
2212
2213 TZDBNameSearchHandler handler(types, fRegion);
2214 gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
2215 if (U_FAILURE(status)) {
2216 return NULL;
2217 }
2218 int32_t maxLen = 0;
2219 return handler.getMatches(maxLen);
2220 }
2221
2222 const TZDBNames*
2223 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
2224 umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
2225 if (U_FAILURE(status)) {
2226 return NULL;
2227 }
2228
2229 TZDBNames* tzdbNames = NULL;
2230
2231 UChar mzIDKey[ZID_KEY_MAX + 1];
2232 mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
2233 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
2234 mzIDKey[mzID.length()] = 0;
2235
2236 umtx_lock(&gTZDBNamesMapLock);
2237 {
2238 void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
2239 if (cacheVal == NULL) {
2240 UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
2241 zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
2242 if (U_SUCCESS(status)) {
2243 char key[ZID_KEY_MAX + 1];
2244 mergeTimeZoneKey(mzID, key);
2245 tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
2246
2247 if (tzdbNames == NULL) {
2248 cacheVal = (void *)EMPTY;
2249 } else {
2250 cacheVal = tzdbNames;
2251 }
2252 // Use the persistent ID as the resource key, so we can
2253 // avoid duplications.
2254 // TODO: Is there a more efficient way, like intern() in Java?
2255 void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
2256 if (newKey != NULL) {
2257 uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
2258 if (U_FAILURE(status)) {
2259 if (tzdbNames != NULL) {
2260 delete tzdbNames;
2261 tzdbNames = NULL;
2262 }
2263 }
2264 } else {
2265 // Should never happen with a valid input
2266 if (tzdbNames != NULL) {
2267 // It's not possible that we get a valid tzdbNames with unknown ID.
2268 // But just in case..
2269 delete tzdbNames;
2270 tzdbNames = NULL;
2271 }
2272 }
2273 }
2274 ures_close(zoneStringsRes);
2275 } else if (cacheVal != EMPTY) {
2276 tzdbNames = (TZDBNames *)cacheVal;
2277 }
2278 }
2279 umtx_unlock(&gTZDBNamesMapLock);
2280
2281 return tzdbNames;
2282 }
2283
2284 U_NAMESPACE_END
2285
2286
2287 #endif /* #if !UCONFIG_NO_FORMATTING */
2288
2289 //eof