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