1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2011-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
9 * File TZNAMES_IMPL.CPP
11 *******************************************************************************
14 #include "unicode/utypes.h"
16 #if !UCONFIG_NO_FORMATTING
18 #include "unicode/strenum.h"
19 #include "unicode/ustring.h"
20 #include "unicode/timezone.h"
21 #include "unicode/utf16.h"
23 #include "tznames_impl.h"
38 #define ZID_KEY_MAX 128
39 #define MZ_PREFIX_LEN 5
41 static const char gZoneStrings
[] = "zoneStrings";
42 static const char gMZPrefix
[] = "meta:";
44 static const char EMPTY
[] = "<empty>"; // place holder for empty ZNames
45 static const char DUMMY_LOADER
[] = "<dummy>"; // place holder for dummy ZNamesLoader
46 static const UChar NO_NAME
[] = { 0 }; // for empty no-fallback time zone names
48 // stuff for TZDBTimeZoneNames
49 static const char* TZDBNAMES_KEYS
[] = {"ss", "sd"};
50 static const int32_t TZDBNAMES_KEYS_SIZE
= UPRV_LENGTHOF(TZDBNAMES_KEYS
);
52 static UMutex
*gDataMutex() {
53 static UMutex
*m
= STATIC_NEW(UMutex
);
57 static UHashtable
* gTZDBNamesMap
= NULL
;
58 static icu::UInitOnce gTZDBNamesMapInitOnce
= U_INITONCE_INITIALIZER
;
60 static TextTrieMap
* gTZDBNamesTrie
= NULL
;
61 static icu::UInitOnce gTZDBNamesTrieInitOnce
= U_INITONCE_INITIALIZER
;
63 // The order in which strings are stored may be different than the order in the public enum.
64 enum UTimeZoneNameTypeIndex
{
65 UTZNM_INDEX_UNKNOWN
= -1,
66 UTZNM_INDEX_EXEMPLAR_LOCATION
,
67 UTZNM_INDEX_LONG_GENERIC
,
68 UTZNM_INDEX_LONG_STANDARD
,
69 UTZNM_INDEX_LONG_DAYLIGHT
,
70 UTZNM_INDEX_SHORT_GENERIC
,
71 UTZNM_INDEX_SHORT_STANDARD
,
72 UTZNM_INDEX_SHORT_DAYLIGHT
,
75 static const UChar
* const EMPTY_NAMES
[UTZNM_INDEX_COUNT
] = {0,0,0,0,0,0,0};
78 static UBool U_CALLCONV
tzdbTimeZoneNames_cleanup(void) {
79 if (gTZDBNamesMap
!= NULL
) {
80 uhash_close(gTZDBNamesMap
);
83 gTZDBNamesMapInitOnce
.reset();
85 if (gTZDBNamesTrie
!= NULL
) {
86 delete gTZDBNamesTrie
;
87 gTZDBNamesTrie
= NULL
;
89 gTZDBNamesTrieInitOnce
.reset();
96 * ZNameInfo stores zone name information in the trie
99 UTimeZoneNameType type
;
105 * ZMatchInfo stores zone name match information used by find method
108 const ZNameInfo
* znameInfo
;
113 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
);
115 #define DEFAULT_CHARACTERNODE_CAPACITY 1
117 // ---------------------------------------------------
118 // CharacterNode class implementation
119 // ---------------------------------------------------
120 void CharacterNode::clear() {
121 uprv_memset(this, 0, sizeof(*this));
124 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
125 if (fValues
== NULL
) {
127 } else if (!fHasValuesVector
) {
129 valueDeleter(fValues
);
132 delete (UVector
*)fValues
;
137 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
138 if (U_FAILURE(status
)) {
144 if (fValues
== NULL
) {
147 // At least one value already.
148 if (!fHasValuesVector
) {
149 // There is only one value so far, and not in a vector yet.
150 // Create a vector and add the old value.
151 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
152 if (U_FAILURE(status
)) {
158 values
->addElement(fValues
, status
);
160 fHasValuesVector
= TRUE
;
162 // Add the new value.
163 ((UVector
*)fValues
)->addElement(value
, status
);
167 // ---------------------------------------------------
168 // TextTrieMapSearchResultHandler class implementation
169 // ---------------------------------------------------
170 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
173 // ---------------------------------------------------
174 // TextTrieMap class implementation
175 // ---------------------------------------------------
176 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
177 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
178 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
181 TextTrieMap::~TextTrieMap() {
183 for (index
= 0; index
< fNodesCount
; ++index
) {
184 fNodes
[index
].deleteValues(fValueDeleter
);
187 if (fLazyContents
!= NULL
) {
188 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
190 fValueDeleter(fLazyContents
->elementAt(i
+1));
193 delete fLazyContents
;
197 int32_t TextTrieMap::isEmpty() const {
198 // Use a separate field for fIsEmpty because it will remain unchanged once the
199 // Trie is built, while fNodes and fLazyContents change with the lazy init
200 // of the nodes structure. Trying to test the changing fields has
201 // thread safety complications.
206 // We defer actually building the TextTrieMap node structure until the first time a
207 // search is performed. put() simply saves the parameters in case we do
208 // eventually need to build it.
211 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
212 const UChar
*s
= sp
.get(key
, status
);
213 put(s
, value
, status
);
216 // This method is designed for a persistent key, such as string key stored in
219 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
221 if (fLazyContents
== NULL
) {
222 fLazyContents
= new UVector(status
);
223 if (fLazyContents
== NULL
) {
224 status
= U_MEMORY_ALLOCATION_ERROR
;
227 if (U_FAILURE(status
)) {
229 fValueDeleter((void*) key
);
233 U_ASSERT(fLazyContents
!= NULL
);
235 UChar
*s
= const_cast<UChar
*>(key
);
236 fLazyContents
->addElement(s
, status
);
237 if (U_FAILURE(status
)) {
239 fValueDeleter((void*) key
);
244 fLazyContents
->addElement(value
, status
);
248 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
249 if (fNodes
== NULL
) {
250 fNodesCapacity
= 512;
251 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
252 if (fNodes
== NULL
) {
253 status
= U_MEMORY_ALLOCATION_ERROR
;
256 fNodes
[0].clear(); // Init root node.
260 UnicodeString foldedKey
;
261 const UChar
*keyBuffer
;
264 // Ok to use fastCopyFrom() because we discard the copy when we return.
265 foldedKey
.fastCopyFrom(key
).foldCase();
266 keyBuffer
= foldedKey
.getBuffer();
267 keyLength
= foldedKey
.length();
269 keyBuffer
= key
.getBuffer();
270 keyLength
= key
.length();
273 CharacterNode
*node
= fNodes
;
275 for (index
= 0; index
< keyLength
; ++index
) {
276 node
= addChildNode(node
, keyBuffer
[index
], status
);
278 node
->addValue(value
, fValueDeleter
, status
);
282 TextTrieMap::growNodes() {
283 if (fNodesCapacity
== 0xffff) {
284 return FALSE
; // We use 16-bit node indexes.
286 int32_t newCapacity
= fNodesCapacity
+ 1000;
287 if (newCapacity
> 0xffff) {
288 newCapacity
= 0xffff;
290 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
291 if (newNodes
== NULL
) {
294 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
297 fNodesCapacity
= newCapacity
;
302 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
303 if (U_FAILURE(status
)) {
306 // Linear search of the sorted list of children.
307 uint16_t prevIndex
= 0;
308 uint16_t nodeIndex
= parent
->fFirstChild
;
309 while (nodeIndex
> 0) {
310 CharacterNode
*current
= fNodes
+ nodeIndex
;
311 UChar childCharacter
= current
->fCharacter
;
312 if (childCharacter
== c
) {
314 } else if (childCharacter
> c
) {
317 prevIndex
= nodeIndex
;
318 nodeIndex
= current
->fNextSibling
;
321 // Ensure capacity. Grow fNodes[] if needed.
322 if (fNodesCount
== fNodesCapacity
) {
323 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
325 status
= U_MEMORY_ALLOCATION_ERROR
;
328 parent
= fNodes
+ parentIndex
;
331 // Insert a new child node with c in sorted order.
332 CharacterNode
*node
= fNodes
+ fNodesCount
;
334 node
->fCharacter
= c
;
335 node
->fNextSibling
= nodeIndex
;
336 if (prevIndex
== 0) {
337 parent
->fFirstChild
= (uint16_t)fNodesCount
;
339 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
346 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
347 // Linear search of the sorted list of children.
348 uint16_t nodeIndex
= parent
->fFirstChild
;
349 while (nodeIndex
> 0) {
350 CharacterNode
*current
= fNodes
+ nodeIndex
;
351 UChar childCharacter
= current
->fCharacter
;
352 if (childCharacter
== c
) {
354 } else if (childCharacter
> c
) {
357 nodeIndex
= current
->fNextSibling
;
363 // buildTrie() - The Trie node structure is needed. Create it from the data that was
364 // saved at the time the ZoneStringFormatter was created. The Trie is only
365 // needed for parsing operations, which are less common than formatting,
366 // and the Trie is big, which is why its creation is deferred until first use.
367 void TextTrieMap::buildTrie(UErrorCode
&status
) {
368 if (fLazyContents
!= NULL
) {
369 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
370 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
371 void *val
= fLazyContents
->elementAt(i
+1);
372 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
373 putImpl(keyString
, val
, status
);
375 delete fLazyContents
;
376 fLazyContents
= NULL
;
381 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
382 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
384 // TODO: if locking the mutex for each check proves to be a performance problem,
385 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
386 // the ICU atomic safe functions for assigning and testing.
387 // Don't test the pointer fLazyContents.
388 // Don't do unless it's really required.
390 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
391 static UMutex
*TextTrieMutex
= STATIC_NEW(UMutex
);
393 Mutex
lock(TextTrieMutex
);
394 if (fLazyContents
!= NULL
) {
395 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
396 nonConstThis
->buildTrie(status
);
399 if (fNodes
== NULL
) {
402 search(fNodes
, text
, start
, start
, handler
, status
);
406 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
407 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
408 if (U_FAILURE(status
)) {
411 if (node
->hasValues()) {
412 if (!handler
->handleMatch(index
- start
, node
, status
)) {
415 if (U_FAILURE(status
)) {
420 // for folding we need to get a complete code point.
421 // size of character may grow after fold operation;
422 // then we need to get result as UTF16 code units.
423 UChar32 c32
= text
.char32At(index
);
424 index
+= U16_LENGTH(c32
);
425 UnicodeString
tmp(c32
);
428 while (tmpidx
< tmp
.length()) {
429 UChar c
= tmp
.charAt(tmpidx
++);
430 node
= getChildNode(node
, c
);
436 // here we just get the next UTF16 code unit
437 UChar c
= text
.charAt(index
++);
438 node
= getChildNode(node
, c
);
441 search(node
, text
, start
, index
, handler
, status
);
445 // ---------------------------------------------------
446 // ZNStringPool class implementation
447 // ---------------------------------------------------
448 static const int32_t POOL_CHUNK_SIZE
= 2000;
449 struct ZNStringPoolChunk
: public UMemory
{
450 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
451 int32_t fLimit
; // Index to start of unused area at end of fStrings
452 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
456 ZNStringPoolChunk::ZNStringPoolChunk() {
461 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
464 if (U_FAILURE(status
)) {
467 fChunks
= new ZNStringPoolChunk
;
468 if (fChunks
== NULL
) {
469 status
= U_MEMORY_ALLOCATION_ERROR
;
473 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
474 uhash_compareUChars
/* keyComp */,
475 uhash_compareUChars
/* valueComp */,
477 if (U_FAILURE(status
)) {
482 ZNStringPool::~ZNStringPool() {
488 while (fChunks
!= NULL
) {
489 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
495 static const UChar EmptyString
= 0;
497 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
498 const UChar
*pooledString
;
499 if (U_FAILURE(status
)) {
503 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
504 if (pooledString
!= NULL
) {
508 int32_t length
= u_strlen(s
);
509 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
510 if (remainingLength
<= length
) {
511 U_ASSERT(length
< POOL_CHUNK_SIZE
);
512 if (length
>= POOL_CHUNK_SIZE
) {
513 status
= U_INTERNAL_PROGRAM_ERROR
;
516 ZNStringPoolChunk
*oldChunk
= fChunks
;
517 fChunks
= new ZNStringPoolChunk
;
518 if (fChunks
== NULL
) {
519 status
= U_MEMORY_ALLOCATION_ERROR
;
522 fChunks
->fNext
= oldChunk
;
525 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
526 u_strcpy(destString
, s
);
527 fChunks
->fLimit
+= (length
+ 1);
528 uhash_put(fHash
, destString
, destString
, &status
);
534 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
535 // into the pool's storage. Used for strings from resource bundles,
536 // which will perisist for the life of the zone string formatter, and
537 // therefore can be used directly without copying.
538 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
539 const UChar
*pooledString
;
540 if (U_FAILURE(status
)) {
544 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
545 if (pooledString
== NULL
) {
546 UChar
*ncs
= const_cast<UChar
*>(s
);
547 uhash_put(fHash
, ncs
, ncs
, &status
);
554 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
555 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
556 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
560 * freeze(). Close the hash table that maps to the pooled strings.
561 * After freezing, the pool can not be searched or added to,
562 * but all existing references to pooled strings remain valid.
564 * The main purpose is to recover the storage used for the hash.
566 void ZNStringPool::freeze() {
573 * This class stores name data for a meta zone or time zone.
575 class ZNames
: public UMemory
{
577 friend class TimeZoneNamesImpl
;
579 static UTimeZoneNameTypeIndex
getTZNameTypeIndex(UTimeZoneNameType type
) {
581 case UTZNM_EXEMPLAR_LOCATION
: return UTZNM_INDEX_EXEMPLAR_LOCATION
;
582 case UTZNM_LONG_GENERIC
: return UTZNM_INDEX_LONG_GENERIC
;
583 case UTZNM_LONG_STANDARD
: return UTZNM_INDEX_LONG_STANDARD
;
584 case UTZNM_LONG_DAYLIGHT
: return UTZNM_INDEX_LONG_DAYLIGHT
;
585 case UTZNM_SHORT_GENERIC
: return UTZNM_INDEX_SHORT_GENERIC
;
586 case UTZNM_SHORT_STANDARD
: return UTZNM_INDEX_SHORT_STANDARD
;
587 case UTZNM_SHORT_DAYLIGHT
: return UTZNM_INDEX_SHORT_DAYLIGHT
;
588 default: return UTZNM_INDEX_UNKNOWN
;
591 static UTimeZoneNameType
getTZNameType(UTimeZoneNameTypeIndex index
) {
593 case UTZNM_INDEX_EXEMPLAR_LOCATION
: return UTZNM_EXEMPLAR_LOCATION
;
594 case UTZNM_INDEX_LONG_GENERIC
: return UTZNM_LONG_GENERIC
;
595 case UTZNM_INDEX_LONG_STANDARD
: return UTZNM_LONG_STANDARD
;
596 case UTZNM_INDEX_LONG_DAYLIGHT
: return UTZNM_LONG_DAYLIGHT
;
597 case UTZNM_INDEX_SHORT_GENERIC
: return UTZNM_SHORT_GENERIC
;
598 case UTZNM_INDEX_SHORT_STANDARD
: return UTZNM_SHORT_STANDARD
;
599 case UTZNM_INDEX_SHORT_DAYLIGHT
: return UTZNM_SHORT_DAYLIGHT
;
600 default: return UTZNM_UNKNOWN
;
604 const UChar
* fNames
[UTZNM_INDEX_COUNT
];
605 UBool fDidAddIntoTrie
;
607 // Whether we own the location string, if computed rather than loaded from a bundle.
608 // A meta zone names instance never has an exemplar location string.
609 UBool fOwnsLocationName
;
611 ZNames(const UChar
* names
[], const UChar
* locationName
)
612 : fDidAddIntoTrie(FALSE
) {
613 uprv_memcpy(fNames
, names
, sizeof(fNames
));
614 if (locationName
!= NULL
) {
615 fOwnsLocationName
= TRUE
;
616 fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
] = locationName
;
618 fOwnsLocationName
= FALSE
;
624 if (fOwnsLocationName
) {
625 const UChar
* locationName
= fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
];
626 U_ASSERT(locationName
!= NULL
);
627 uprv_free((void*) locationName
);
632 static void* createMetaZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
633 const UnicodeString
& mzID
, UErrorCode
& status
) {
634 if (U_FAILURE(status
)) { return NULL
; }
635 U_ASSERT(names
!= NULL
);
637 // Use the persistent ID as the resource key, so we can
638 // avoid duplications.
639 // TODO: Is there a more efficient way, like intern() in Java?
640 void* key
= (void*) ZoneMeta::findMetaZoneID(mzID
);
642 if (uprv_memcmp(names
, EMPTY_NAMES
, sizeof(EMPTY_NAMES
)) == 0) {
643 value
= (void*) EMPTY
;
645 value
= (void*) (new ZNames(names
, NULL
));
647 status
= U_MEMORY_ALLOCATION_ERROR
;
651 uhash_put(cache
, key
, value
, &status
);
655 static void* createTimeZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
656 const UnicodeString
& tzID
, UErrorCode
& status
) {
657 if (U_FAILURE(status
)) { return NULL
; }
658 U_ASSERT(names
!= NULL
);
660 // If necessary, compute the location name from the time zone name.
661 UChar
* locationName
= NULL
;
662 if (names
[UTZNM_INDEX_EXEMPLAR_LOCATION
] == NULL
) {
663 UnicodeString locationNameUniStr
;
664 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, locationNameUniStr
);
666 // Copy the computed location name to the heap
667 if (locationNameUniStr
.length() > 0) {
668 const UChar
* buff
= locationNameUniStr
.getTerminatedBuffer();
669 int32_t len
= sizeof(UChar
) * (locationNameUniStr
.length() + 1);
670 locationName
= (UChar
*) uprv_malloc(len
);
671 if (locationName
== NULL
) {
672 status
= U_MEMORY_ALLOCATION_ERROR
;
675 uprv_memcpy(locationName
, buff
, len
);
679 // Use the persistent ID as the resource key, so we can
680 // avoid duplications.
681 // TODO: Is there a more efficient way, like intern() in Java?
682 void* key
= (void*) ZoneMeta::findTimeZoneID(tzID
);
683 void* value
= (void*) (new ZNames(names
, locationName
));
685 status
= U_MEMORY_ALLOCATION_ERROR
;
688 uhash_put(cache
, key
, value
, &status
);
692 const UChar
* getName(UTimeZoneNameType type
) const {
693 UTimeZoneNameTypeIndex index
= getTZNameTypeIndex(type
);
694 return index
>= 0 ? fNames
[index
] : NULL
;
697 void addAsMetaZoneIntoTrie(const UChar
* mzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
698 addNamesIntoTrie(mzID
, NULL
, trie
, status
);
700 void addAsTimeZoneIntoTrie(const UChar
* tzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
701 addNamesIntoTrie(NULL
, tzID
, trie
, status
);
704 void addNamesIntoTrie(const UChar
* mzID
, const UChar
* tzID
, TextTrieMap
& trie
,
705 UErrorCode
& status
) {
706 if (U_FAILURE(status
)) { return; }
707 if (fDidAddIntoTrie
) { return; }
708 fDidAddIntoTrie
= TRUE
;
710 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; i
++) {
711 const UChar
* name
= fNames
[i
];
713 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
714 if (nameinfo
== NULL
) {
715 status
= U_MEMORY_ALLOCATION_ERROR
;
718 nameinfo
->mzID
= mzID
;
719 nameinfo
->tzID
= tzID
;
720 nameinfo
->type
= getTZNameType((UTimeZoneNameTypeIndex
)i
);
721 trie
.put(name
, nameinfo
, status
); // trie.put() takes ownership of the key
722 if (U_FAILURE(status
)) {
733 struct ZNames::ZNamesLoader
: public ResourceSink
{
734 const UChar
*names
[UTZNM_INDEX_COUNT
];
739 virtual ~ZNamesLoader();
741 /** Reset for loading another set of names. */
743 uprv_memcpy(names
, EMPTY_NAMES
, sizeof(names
));
746 void loadMetaZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& mzID
, UErrorCode
& errorCode
) {
747 if (U_FAILURE(errorCode
)) { return; }
749 char key
[ZID_KEY_MAX
+ 1];
750 mergeTimeZoneKey(mzID
, key
);
752 loadNames(zoneStrings
, key
, errorCode
);
755 void loadTimeZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& tzID
, UErrorCode
& errorCode
) {
756 // Replace "/" with ":".
757 UnicodeString
uKey(tzID
);
758 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
759 if (uKey
.charAt(i
) == (UChar
)0x2F) {
760 uKey
.setCharAt(i
, (UChar
)0x3A);
764 char key
[ZID_KEY_MAX
+ 1];
765 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
767 loadNames(zoneStrings
, key
, errorCode
);
770 void loadNames(const UResourceBundle
* zoneStrings
, const char* key
, UErrorCode
& errorCode
) {
771 U_ASSERT(zoneStrings
!= NULL
);
772 U_ASSERT(key
!= NULL
);
773 U_ASSERT(key
[0] != '\0');
775 UErrorCode localStatus
= U_ZERO_ERROR
;
777 ures_getAllItemsWithFallback(zoneStrings
, key
, *this, localStatus
);
779 // Ignore errors, but propogate possible warnings.
780 if (U_SUCCESS(localStatus
)) {
781 errorCode
= localStatus
;
785 void setNameIfEmpty(const char* key
, const ResourceValue
* value
, UErrorCode
& errorCode
) {
786 UTimeZoneNameTypeIndex type
= nameTypeFromKey(key
);
787 if (type
== UTZNM_INDEX_UNKNOWN
) { return; }
788 if (names
[type
] == NULL
) {
790 // 'NO_NAME' indicates internally that this field should remain empty. It will be
791 // replaced by 'NULL' in getNames()
792 names
[type
] = (value
== NULL
) ? NO_NAME
: value
->getString(length
, errorCode
);
796 virtual void put(const char* key
, ResourceValue
& value
, UBool
/*noFallback*/,
797 UErrorCode
&errorCode
) {
798 ResourceTable namesTable
= value
.getTable(errorCode
);
799 if (U_FAILURE(errorCode
)) { return; }
800 for (int32_t i
= 0; namesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
801 if (value
.isNoInheritanceMarker()) {
802 setNameIfEmpty(key
, NULL
, errorCode
);
804 setNameIfEmpty(key
, &value
, errorCode
);
809 static UTimeZoneNameTypeIndex
nameTypeFromKey(const char *key
) {
811 if ((c0
= key
[0]) == 0 || (c1
= key
[1]) == 0 || key
[2] != 0) {
812 return UTZNM_INDEX_UNKNOWN
;
815 return c1
== 'g' ? UTZNM_INDEX_LONG_GENERIC
:
816 c1
== 's' ? UTZNM_INDEX_LONG_STANDARD
:
817 c1
== 'd' ? UTZNM_INDEX_LONG_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
818 } else if (c0
== 's') {
819 return c1
== 'g' ? UTZNM_INDEX_SHORT_GENERIC
:
820 c1
== 's' ? UTZNM_INDEX_SHORT_STANDARD
:
821 c1
== 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
822 } else if (c0
== 'e' && c1
== 'c') {
823 return UTZNM_INDEX_EXEMPLAR_LOCATION
;
825 return UTZNM_INDEX_UNKNOWN
;
829 * Returns an array of names. It is the caller's responsibility to copy the data into a
830 * permanent location, as the returned array is owned by the loader instance and may be
831 * cleared or leave scope.
833 * This is different than Java, where the array will no longer be modified and null
836 const UChar
** getNames() {
837 // Remove 'NO_NAME' references in the array and replace with 'NULL'
838 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; ++i
) {
839 if (names
[i
] == NO_NAME
) {
847 ZNames::ZNamesLoader::~ZNamesLoader() {}
850 // ---------------------------------------------------
851 // The meta zone ID enumeration class
852 // ---------------------------------------------------
853 class MetaZoneIDsEnumeration
: public StringEnumeration
{
855 MetaZoneIDsEnumeration();
856 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
857 MetaZoneIDsEnumeration(UVector
* mzIDs
);
858 virtual ~MetaZoneIDsEnumeration();
859 static UClassID U_EXPORT2
getStaticClassID(void);
860 virtual UClassID
getDynamicClassID(void) const;
861 virtual const UnicodeString
* snext(UErrorCode
& status
);
862 virtual void reset(UErrorCode
& status
);
863 virtual int32_t count(UErrorCode
& status
) const;
867 const UVector
* fMetaZoneIDs
;
868 UVector
*fLocalVector
;
871 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
873 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
874 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
877 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
878 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
879 fLen
= fMetaZoneIDs
->size();
882 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
883 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
885 fLen
= fMetaZoneIDs
->size();
890 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
891 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
892 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
899 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
904 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
908 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
915 // ---------------------------------------------------
916 // ZNameSearchHandler
917 // ---------------------------------------------------
918 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
920 ZNameSearchHandler(uint32_t types
);
921 virtual ~ZNameSearchHandler();
923 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
924 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
928 int32_t fMaxMatchLen
;
929 TimeZoneNames::MatchInfoCollection
* fResults
;
932 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
933 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
936 ZNameSearchHandler::~ZNameSearchHandler() {
937 if (fResults
!= NULL
) {
943 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
944 if (U_FAILURE(status
)) {
947 if (node
->hasValues()) {
948 int32_t valuesCount
= node
->countValues();
949 for (int32_t i
= 0; i
< valuesCount
; i
++) {
950 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
951 if (nameinfo
== NULL
) {
954 if ((nameinfo
->type
& fTypes
) != 0) {
955 // matches a requested type
956 if (fResults
== NULL
) {
957 fResults
= new TimeZoneNames::MatchInfoCollection();
958 if (fResults
== NULL
) {
959 status
= U_MEMORY_ALLOCATION_ERROR
;
962 if (U_SUCCESS(status
)) {
963 U_ASSERT(fResults
!= NULL
);
964 if (nameinfo
->tzID
) {
965 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
967 U_ASSERT(nameinfo
->mzID
);
968 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
970 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
971 fMaxMatchLen
= matchLength
;
980 TimeZoneNames::MatchInfoCollection
*
981 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
982 // give the ownership to the caller
983 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
984 maxMatchLen
= fMaxMatchLen
;
992 // ---------------------------------------------------
995 // TimeZoneNames implementation class. This is the main
996 // part of this module.
997 // ---------------------------------------------------
1001 * Deleter for ZNames
1003 static void U_CALLCONV
1004 deleteZNames(void *obj
) {
1006 delete (ZNames
*) obj
;
1011 * Deleter for ZNameInfo
1013 static void U_CALLCONV
1014 deleteZNameInfo(void *obj
) {
1020 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
1025 fNamesTrieFullyLoaded(FALSE
),
1026 fNamesFullyLoaded(FALSE
),
1027 fNamesTrie(TRUE
, deleteZNameInfo
) {
1028 initialize(locale
, status
);
1032 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
1033 if (U_FAILURE(status
)) {
1037 // Load zoneStrings bundle
1038 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
1039 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
1040 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
1041 if (U_FAILURE(tmpsts
)) {
1047 // Initialize hashtables holding time zone/meta zone names
1048 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1049 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1050 if (U_FAILURE(status
)) {
1055 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
1056 uhash_setValueDeleter(fTZNamesMap
, deleteZNames
);
1057 // no key deleters for name maps
1059 // preload zone strings for the default zone
1060 TimeZone
*tz
= TimeZone::createDefault();
1061 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
1063 loadStrings(UnicodeString(tzID
), status
);
1071 * This method updates the cache and must be called with a lock,
1072 * except initializer.
1075 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
, UErrorCode
& status
) {
1076 loadTimeZoneNames(tzCanonicalID
, status
);
1077 LocalPointer
<StringEnumeration
> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID
, status
));
1078 if (U_FAILURE(status
)) { return; }
1079 U_ASSERT(!mzIDs
.isNull());
1081 const UnicodeString
*mzID
;
1082 while (((mzID
= mzIDs
->snext(status
)) != NULL
) && U_SUCCESS(status
)) {
1083 loadMetaZoneNames(*mzID
, status
);
1087 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1092 TimeZoneNamesImpl::cleanup() {
1093 if (fZoneStrings
!= NULL
) {
1094 ures_close(fZoneStrings
);
1095 fZoneStrings
= NULL
;
1097 if (fMZNamesMap
!= NULL
) {
1098 uhash_close(fMZNamesMap
);
1101 if (fTZNamesMap
!= NULL
) {
1102 uhash_close(fTZNamesMap
);
1108 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
1109 if (this == &other
) {
1112 // No implementation for now
1117 TimeZoneNamesImpl::clone() const {
1118 UErrorCode status
= U_ZERO_ERROR
;
1119 return new TimeZoneNamesImpl(fLocale
, status
);
1123 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
1124 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1127 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1129 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode
& status
) {
1130 if (U_FAILURE(status
)) {
1133 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
1134 if (mzIDs
== NULL
) {
1135 return new MetaZoneIDsEnumeration();
1137 return new MetaZoneIDsEnumeration(*mzIDs
);
1141 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1142 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
1145 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1147 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) {
1148 if (U_FAILURE(status
)) {
1151 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1152 if (mappings
== NULL
) {
1153 return new MetaZoneIDsEnumeration();
1156 MetaZoneIDsEnumeration
*senum
= NULL
;
1157 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1158 if (mzIDs
== NULL
) {
1159 status
= U_MEMORY_ALLOCATION_ERROR
;
1161 if (U_SUCCESS(status
)) {
1162 U_ASSERT(mzIDs
!= NULL
);
1163 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1165 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1166 const UChar
*mzID
= map
->mzid
;
1167 if (!mzIDs
->contains((void *)mzID
)) {
1168 mzIDs
->addElement((void *)mzID
, status
);
1171 if (U_SUCCESS(status
)) {
1172 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1181 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1182 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
1185 // static implementation of getMetaZoneID
1187 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) {
1188 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1193 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1194 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
1197 // static implementaion of getReferenceZoneID
1199 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) {
1200 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1205 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1206 UTimeZoneNameType type
,
1207 UnicodeString
& name
) const {
1208 name
.setToBogus(); // cleanup result.
1209 if (mzID
.isEmpty()) {
1213 ZNames
*znames
= NULL
;
1214 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1217 Mutex
lock(gDataMutex());
1218 UErrorCode status
= U_ZERO_ERROR
;
1219 znames
= nonConstThis
->loadMetaZoneNames(mzID
, status
);
1220 if (U_FAILURE(status
)) { return name
; }
1223 if (znames
!= NULL
) {
1224 const UChar
* s
= znames
->getName(type
);
1226 name
.setTo(TRUE
, s
, -1);
1233 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1234 name
.setToBogus(); // cleanup result.
1235 if (tzID
.isEmpty()) {
1239 ZNames
*tznames
= NULL
;
1240 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1243 Mutex
lock(gDataMutex());
1244 UErrorCode status
= U_ZERO_ERROR
;
1245 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1246 if (U_FAILURE(status
)) { return name
; }
1249 if (tznames
!= NULL
) {
1250 const UChar
*s
= tznames
->getName(type
);
1252 name
.setTo(TRUE
, s
, -1);
1259 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1260 name
.setToBogus(); // cleanup result.
1261 const UChar
* locName
= NULL
;
1262 ZNames
*tznames
= NULL
;
1263 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1266 Mutex
lock(gDataMutex());
1267 UErrorCode status
= U_ZERO_ERROR
;
1268 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1269 if (U_FAILURE(status
)) { return name
; }
1272 if (tznames
!= NULL
) {
1273 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1275 if (locName
!= NULL
) {
1276 name
.setTo(TRUE
, locName
, -1);
1283 // Merge the MZ_PREFIX and mzId
1284 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1285 if (mzID
.isEmpty()) {
1290 char mzIdChar
[ZID_KEY_MAX
+ 1];
1292 int32_t prefixLen
= static_cast<int32_t>(uprv_strlen(gMZPrefix
));
1293 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1294 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1295 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1296 result
[keyLen
+ prefixLen
] = '\0';
1300 * This method updates the cache and must be called with a lock
1303 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
1304 if (U_FAILURE(status
)) { return NULL
; }
1305 U_ASSERT(mzID
.length() <= ZID_KEY_MAX
- MZ_PREFIX_LEN
);
1307 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1308 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1309 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1310 mzIDKey
[mzID
.length()] = 0;
1312 void* mznames
= uhash_get(fMZNamesMap
, mzIDKey
);
1313 if (mznames
== NULL
) {
1314 ZNames::ZNamesLoader loader
;
1315 loader
.loadMetaZone(fZoneStrings
, mzID
, status
);
1316 mznames
= ZNames::createMetaZoneAndPutInCache(fMZNamesMap
, loader
.getNames(), mzID
, status
);
1317 if (U_FAILURE(status
)) { return NULL
; }
1320 if (mznames
!= EMPTY
) {
1321 return (ZNames
*)mznames
;
1328 * This method updates the cache and must be called with a lock
1331 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
, UErrorCode
& status
) {
1332 if (U_FAILURE(status
)) { return NULL
; }
1333 U_ASSERT(tzID
.length() <= ZID_KEY_MAX
);
1335 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1336 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1337 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1338 tzIDKey
[tzIDKeyLen
] = 0;
1340 void *tznames
= uhash_get(fTZNamesMap
, tzIDKey
);
1341 if (tznames
== NULL
) {
1342 ZNames::ZNamesLoader loader
;
1343 loader
.loadTimeZone(fZoneStrings
, tzID
, status
);
1344 tznames
= ZNames::createTimeZoneAndPutInCache(fTZNamesMap
, loader
.getNames(), tzID
, status
);
1345 if (U_FAILURE(status
)) { return NULL
; }
1348 // tznames is never EMPTY
1349 return (ZNames
*)tznames
;
1352 TimeZoneNames::MatchInfoCollection
*
1353 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1354 ZNameSearchHandler
handler(types
);
1355 TimeZoneNames::MatchInfoCollection
* matches
;
1356 TimeZoneNamesImpl
* nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1358 // Synchronize so that data is not loaded multiple times.
1359 // TODO: Consider more fine-grained synchronization.
1361 Mutex
lock(gDataMutex());
1363 // First try of lookup.
1364 matches
= doFind(handler
, text
, start
, status
);
1365 if (U_FAILURE(status
)) { return NULL
; }
1366 if (matches
!= NULL
) {
1370 // All names are not yet loaded into the trie.
1371 // We may have loaded names for formatting several time zones,
1372 // and might be parsing one of those.
1373 // Populate the parsing trie from all of the already-loaded names.
1374 nonConstThis
->addAllNamesIntoTrie(status
);
1376 // Second try of lookup.
1377 matches
= doFind(handler
, text
, start
, status
);
1378 if (U_FAILURE(status
)) { return NULL
; }
1379 if (matches
!= NULL
) {
1383 // There are still some names we haven't loaded into the trie yet.
1384 // Load everything now.
1385 nonConstThis
->internalLoadAllDisplayNames(status
);
1386 nonConstThis
->addAllNamesIntoTrie(status
);
1387 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1388 if (U_FAILURE(status
)) { return NULL
; }
1390 // Third try: we must return this one.
1391 return doFind(handler
, text
, start
, status
);
1395 TimeZoneNames::MatchInfoCollection
*
1396 TimeZoneNamesImpl::doFind(ZNameSearchHandler
& handler
,
1397 const UnicodeString
& text
, int32_t start
, UErrorCode
& status
) const {
1399 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1400 if (U_FAILURE(status
)) { return NULL
; }
1403 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1404 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1405 // perfect match, or no more names available
1412 // Caller must synchronize.
1413 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode
& status
) {
1414 if (U_FAILURE(status
)) return;
1416 const UHashElement
* element
;
1419 while ((element
= uhash_nextElement(fMZNamesMap
, &pos
)) != NULL
) {
1420 if (element
->value
.pointer
== EMPTY
) { continue; }
1421 UChar
* mzID
= (UChar
*) element
->key
.pointer
;
1422 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1423 znames
->addAsMetaZoneIntoTrie(mzID
, fNamesTrie
, status
);
1424 if (U_FAILURE(status
)) { return; }
1428 while ((element
= uhash_nextElement(fTZNamesMap
, &pos
)) != NULL
) {
1429 if (element
->value
.pointer
== EMPTY
) { continue; }
1430 UChar
* tzID
= (UChar
*) element
->key
.pointer
;
1431 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1432 znames
->addAsTimeZoneIntoTrie(tzID
, fNamesTrie
, status
);
1433 if (U_FAILURE(status
)) { return; }
1438 static void U_CALLCONV
1439 deleteZNamesLoader(void* obj
) {
1440 if (obj
== DUMMY_LOADER
) { return; }
1441 const ZNames::ZNamesLoader
* loader
= (const ZNames::ZNamesLoader
*) obj
;
1446 struct TimeZoneNamesImpl::ZoneStringsLoader
: public ResourceSink
{
1447 TimeZoneNamesImpl
& tzn
;
1448 UHashtable
* keyToLoader
;
1450 ZoneStringsLoader(TimeZoneNamesImpl
& _tzn
, UErrorCode
& status
)
1452 keyToLoader
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
1453 if (U_FAILURE(status
)) { return; }
1454 uhash_setKeyDeleter(keyToLoader
, uprv_free
);
1455 uhash_setValueDeleter(keyToLoader
, deleteZNamesLoader
);
1457 virtual ~ZoneStringsLoader();
1459 void* createKey(const char* key
, UErrorCode
& status
) {
1460 int32_t len
= sizeof(char) * (static_cast<int32_t>(uprv_strlen(key
)) + 1);
1461 char* newKey
= (char*) uprv_malloc(len
);
1462 if (newKey
== NULL
) {
1463 status
= U_MEMORY_ALLOCATION_ERROR
;
1466 uprv_memcpy(newKey
, key
, len
);
1467 newKey
[len
-1] = '\0';
1468 return (void*) newKey
;
1471 UBool
isMetaZone(const char* key
) {
1472 return (uprv_strlen(key
) >= MZ_PREFIX_LEN
&& uprv_memcmp(key
, gMZPrefix
, MZ_PREFIX_LEN
) == 0);
1475 UnicodeString
mzIDFromKey(const char* key
) {
1476 return UnicodeString(key
+ MZ_PREFIX_LEN
, static_cast<int32_t>(uprv_strlen(key
)) - MZ_PREFIX_LEN
, US_INV
);
1479 UnicodeString
tzIDFromKey(const char* key
) {
1480 UnicodeString
tzID(key
, -1, US_INV
);
1481 // Replace all colons ':' with slashes '/'
1482 for (int i
=0; i
<tzID
.length(); i
++) {
1483 if (tzID
.charAt(i
) == 0x003A) {
1484 tzID
.setCharAt(i
, 0x002F);
1490 void load(UErrorCode
& status
) {
1491 ures_getAllItemsWithFallback(tzn
.fZoneStrings
, "", *this, status
);
1492 if (U_FAILURE(status
)) { return; }
1494 int32_t pos
= UHASH_FIRST
;
1495 const UHashElement
* element
;
1496 while ((element
= uhash_nextElement(keyToLoader
, &pos
)) != NULL
) {
1497 if (element
->value
.pointer
== DUMMY_LOADER
) { continue; }
1498 ZNames::ZNamesLoader
* loader
= (ZNames::ZNamesLoader
*) element
->value
.pointer
;
1499 char* key
= (char*) element
->key
.pointer
;
1501 if (isMetaZone(key
)) {
1502 UnicodeString mzID
= mzIDFromKey(key
);
1503 ZNames::createMetaZoneAndPutInCache(tzn
.fMZNamesMap
, loader
->getNames(), mzID
, status
);
1505 UnicodeString tzID
= tzIDFromKey(key
);
1506 ZNames::createTimeZoneAndPutInCache(tzn
.fTZNamesMap
, loader
->getNames(), tzID
, status
);
1508 if (U_FAILURE(status
)) { return; }
1512 void consumeNamesTable(const char *key
, ResourceValue
&value
, UBool noFallback
,
1513 UErrorCode
&status
) {
1514 if (U_FAILURE(status
)) { return; }
1516 void* loader
= uhash_get(keyToLoader
, key
);
1517 if (loader
== NULL
) {
1518 if (isMetaZone(key
)) {
1519 UnicodeString mzID
= mzIDFromKey(key
);
1520 void* cacheVal
= uhash_get(tzn
.fMZNamesMap
, mzID
.getTerminatedBuffer());
1521 if (cacheVal
!= NULL
) {
1522 // We have already loaded the names for this meta zone.
1523 loader
= (void*) DUMMY_LOADER
;
1525 loader
= (void*) new ZNames::ZNamesLoader();
1526 if (loader
== NULL
) {
1527 status
= U_MEMORY_ALLOCATION_ERROR
;
1532 UnicodeString tzID
= tzIDFromKey(key
);
1533 void* cacheVal
= uhash_get(tzn
.fTZNamesMap
, tzID
.getTerminatedBuffer());
1534 if (cacheVal
!= NULL
) {
1535 // We have already loaded the names for this time zone.
1536 loader
= (void*) DUMMY_LOADER
;
1538 loader
= (void*) new ZNames::ZNamesLoader();
1539 if (loader
== NULL
) {
1540 status
= U_MEMORY_ALLOCATION_ERROR
;
1546 void* newKey
= createKey(key
, status
);
1547 if (U_FAILURE(status
)) {
1548 deleteZNamesLoader(loader
);
1552 uhash_put(keyToLoader
, newKey
, loader
, &status
);
1553 if (U_FAILURE(status
)) { return; }
1556 if (loader
!= DUMMY_LOADER
) {
1557 // Let the ZNamesLoader consume the names table.
1558 ((ZNames::ZNamesLoader
*)loader
)->put(key
, value
, noFallback
, status
);
1562 virtual void put(const char *key
, ResourceValue
&value
, UBool noFallback
,
1563 UErrorCode
&status
) {
1564 ResourceTable timeZonesTable
= value
.getTable(status
);
1565 if (U_FAILURE(status
)) { return; }
1566 for (int32_t i
= 0; timeZonesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
1567 U_ASSERT(!value
.isNoInheritanceMarker());
1568 if (value
.getType() == URES_TABLE
) {
1569 consumeNamesTable(key
, value
, noFallback
, status
);
1571 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1572 // All time zone fields are tables.
1574 if (U_FAILURE(status
)) { return; }
1579 // Virtual destructors must be defined out of line.
1580 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1581 uhash_close(keyToLoader
);
1584 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode
& status
) {
1585 if (U_FAILURE(status
)) return;
1588 Mutex
lock(gDataMutex());
1589 internalLoadAllDisplayNames(status
);
1593 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString
& tzID
,
1594 const UTimeZoneNameType types
[], int32_t numTypes
,
1595 UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
1596 if (U_FAILURE(status
)) return;
1598 if (tzID
.isEmpty()) { return; }
1599 void* tznames
= NULL
;
1600 void* mznames
= NULL
;
1601 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1603 // Load the time zone strings
1605 Mutex
lock(gDataMutex());
1606 tznames
= (void*) nonConstThis
->loadTimeZoneNames(tzID
, status
);
1607 if (U_FAILURE(status
)) { return; }
1609 U_ASSERT(tznames
!= NULL
);
1611 // Load the values into the dest array
1612 for (int i
= 0; i
< numTypes
; i
++) {
1613 UTimeZoneNameType type
= types
[i
];
1614 const UChar
* name
= ((ZNames
*)tznames
)->getName(type
);
1616 if (mznames
== NULL
) {
1617 // Load the meta zone name
1619 getMetaZoneID(tzID
, date
, mzID
);
1620 if (mzID
.isEmpty()) {
1621 mznames
= (void*) EMPTY
;
1623 // Load the meta zone strings
1624 // Mutex is scoped to the "else" statement
1625 Mutex
lock(gDataMutex());
1626 mznames
= (void*) nonConstThis
->loadMetaZoneNames(mzID
, status
);
1627 if (U_FAILURE(status
)) { return; }
1628 // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1629 // a dummy object instead of NULL.
1630 if (mznames
== NULL
) {
1631 mznames
= (void*) EMPTY
;
1635 U_ASSERT(mznames
!= NULL
);
1636 if (mznames
!= EMPTY
) {
1637 name
= ((ZNames
*)mznames
)->getName(type
);
1641 dest
[i
].setTo(TRUE
, name
, -1);
1643 dest
[i
].setToBogus();
1648 // Caller must synchronize.
1649 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode
& status
) {
1650 if (!fNamesFullyLoaded
) {
1651 fNamesFullyLoaded
= TRUE
;
1653 ZoneStringsLoader
loader(*this, status
);
1654 loader
.load(status
);
1655 if (U_FAILURE(status
)) { return; }
1657 const UnicodeString
*id
;
1659 // load strings for all zones
1660 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(
1661 UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1662 if (U_SUCCESS(status
)) {
1663 while ((id
= tzIDs
->snext(status
)) != NULL
) {
1664 if (U_FAILURE(status
)) {
1667 UnicodeString
copy(*id
);
1668 void* value
= uhash_get(fTZNamesMap
, copy
.getTerminatedBuffer());
1669 if (value
== NULL
) {
1670 // loadStrings also loads related metazone strings
1671 loadStrings(*id
, status
);
1675 if (tzIDs
!= NULL
) {
1683 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1684 static const int32_t gEtcPrefixLen
= 4;
1685 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1686 static const int32_t gSystemVPrefixLen
= 8;
1687 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1688 static const int32_t gRiyadh8Len
= 7;
1690 UnicodeString
& U_EXPORT2
1691 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1692 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1693 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1698 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1699 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1700 name
.setTo(tzID
, sep
+ 1);
1701 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1702 UnicodeString((UChar
)0x20 /* space */));
1709 // ---------------------------------------------------
1710 // TZDBTimeZoneNames and its supporting classes
1712 // TZDBTimeZoneNames is an implementation class of
1713 // TimeZoneNames holding the IANA tz database abbreviations.
1714 // ---------------------------------------------------
1716 class TZDBNames
: public UMemory
{
1718 virtual ~TZDBNames();
1720 static TZDBNames
* createInstance(UResourceBundle
* rb
, const char* key
);
1721 const UChar
* getName(UTimeZoneNameType type
) const;
1722 const char** getParseRegions(int32_t& numRegions
) const;
1725 TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
);
1728 const UChar
** fNames
;
1730 int32_t fNumRegions
;
1733 TZDBNames::TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
)
1736 fNumRegions(numRegions
) {
1739 TZDBNames::~TZDBNames() {
1740 if (fNames
!= NULL
) {
1743 if (fRegions
!= NULL
) {
1744 char **p
= fRegions
;
1745 for (int32_t i
= 0; i
< fNumRegions
; p
++, i
++) {
1748 uprv_free(fRegions
);
1753 TZDBNames::createInstance(UResourceBundle
* rb
, const char* key
) {
1754 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
1758 UErrorCode status
= U_ZERO_ERROR
;
1760 const UChar
**names
= NULL
;
1761 char** regions
= NULL
;
1762 int32_t numRegions
= 0;
1766 UResourceBundle
* rbTable
= NULL
;
1767 rbTable
= ures_getByKey(rb
, key
, rbTable
, &status
);
1768 if (U_FAILURE(status
)) {
1772 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * TZDBNAMES_KEYS_SIZE
);
1773 UBool isEmpty
= TRUE
;
1774 if (names
!= NULL
) {
1775 for (int32_t i
= 0; i
< TZDBNAMES_KEYS_SIZE
; i
++) {
1776 status
= U_ZERO_ERROR
;
1777 const UChar
*value
= ures_getStringByKey(rbTable
, TZDBNAMES_KEYS
[i
], &len
, &status
);
1778 if (U_FAILURE(status
) || len
== 0) {
1788 if (names
!= NULL
) {
1794 UResourceBundle
*regionsRes
= ures_getByKey(rbTable
, "parseRegions", NULL
, &status
);
1795 UBool regionError
= FALSE
;
1796 if (U_SUCCESS(status
)) {
1797 numRegions
= ures_getSize(regionsRes
);
1798 if (numRegions
> 0) {
1799 regions
= (char**)uprv_malloc(sizeof(char*) * numRegions
);
1800 if (regions
!= NULL
) {
1801 char **pRegion
= regions
;
1802 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1807 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1808 status
= U_ZERO_ERROR
;
1809 const UChar
*uregion
= ures_getStringByIndex(regionsRes
, i
, &len
, &status
);
1810 if (U_FAILURE(status
)) {
1814 *pRegion
= (char*)uprv_malloc(sizeof(char) * (len
+ 1));
1815 if (*pRegion
== NULL
) {
1819 u_UCharsToChars(uregion
, *pRegion
, len
);
1820 (*pRegion
)[len
] = 0;
1825 ures_close(regionsRes
);
1826 ures_close(rbTable
);
1829 if (names
!= NULL
) {
1832 if (regions
!= NULL
) {
1834 for (int32_t i
= 0; i
< numRegions
; p
++, i
++) {
1842 return new TZDBNames(names
, regions
, numRegions
);
1846 TZDBNames::getName(UTimeZoneNameType type
) const {
1847 if (fNames
== NULL
) {
1850 const UChar
*name
= NULL
;
1852 case UTZNM_SHORT_STANDARD
:
1855 case UTZNM_SHORT_DAYLIGHT
:
1865 TZDBNames::getParseRegions(int32_t& numRegions
) const {
1866 if (fRegions
== NULL
) {
1869 numRegions
= fNumRegions
;
1871 return (const char**)fRegions
;
1876 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1879 typedef struct TZDBNameInfo
{
1881 UTimeZoneNameType type
;
1882 UBool ambiguousType
;
1883 const char** parseRegions
;
1889 class TZDBNameSearchHandler
: public TextTrieMapSearchResultHandler
{
1891 TZDBNameSearchHandler(uint32_t types
, const char* region
);
1892 virtual ~TZDBNameSearchHandler();
1894 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
1895 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
1899 int32_t fMaxMatchLen
;
1900 TimeZoneNames::MatchInfoCollection
* fResults
;
1901 const char* fRegion
;
1904 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types
, const char* region
)
1905 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
), fRegion(region
) {
1908 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1909 if (fResults
!= NULL
) {
1915 TZDBNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
1916 if (U_FAILURE(status
)) {
1920 TZDBNameInfo
*match
= NULL
;
1921 TZDBNameInfo
*defaultRegionMatch
= NULL
;
1923 if (node
->hasValues()) {
1924 int32_t valuesCount
= node
->countValues();
1925 for (int32_t i
= 0; i
< valuesCount
; i
++) {
1926 TZDBNameInfo
*ninfo
= (TZDBNameInfo
*)node
->getValue(i
);
1927 if (ninfo
== NULL
) {
1930 if ((ninfo
->type
& fTypes
) != 0) {
1931 // Some tz database abbreviations are ambiguous. For example,
1932 // CST means either Central Standard Time or China Standard Time.
1933 // Unlike CLDR time zone display names, this implementation
1934 // does not use unique names. And TimeZoneFormat does not expect
1935 // multiple results returned for the same time zone type.
1936 // For this reason, this implementation resolve one among same
1937 // zone type with a same name at this level.
1938 if (ninfo
->parseRegions
== NULL
) {
1939 // parseRegions == null means this is the default metazone
1940 // mapping for the abbreviation.
1941 if (defaultRegionMatch
== NULL
) {
1942 match
= defaultRegionMatch
= ninfo
;
1945 UBool matchRegion
= FALSE
;
1946 // non-default metazone mapping for an abbreviation
1947 // comes with applicable regions. For example, the default
1948 // metazone mapping for "CST" is America_Central,
1949 // but if region is one of CN/MO/TW, "CST" is parsed
1950 // as metazone China (China Standard Time).
1951 for (int32_t j
= 0; j
< ninfo
->nRegions
; j
++) {
1952 const char *region
= ninfo
->parseRegions
[j
];
1953 if (uprv_strcmp(fRegion
, region
) == 0) {
1962 if (match
== NULL
) {
1969 if (match
!= NULL
) {
1970 UTimeZoneNameType ntype
= match
->type
;
1971 // Note: Workaround for duplicated standard/daylight names
1972 // The tz database contains a few zones sharing a
1973 // same name for both standard time and daylight saving
1974 // time. For example, Australia/Sydney observes DST,
1975 // but "EST" is used for both standard and daylight.
1976 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1977 // in the find operation, we cannot tell which one was
1978 // actually matched.
1979 // TimeZoneFormat#parse returns a matched name type (standard
1980 // or daylight) and DateFormat implementation uses the info to
1981 // to adjust actual time. To avoid false type information,
1982 // this implementation replaces the name type with SHORT_GENERIC.
1983 if (match
->ambiguousType
1984 && (ntype
== UTZNM_SHORT_STANDARD
|| ntype
== UTZNM_SHORT_DAYLIGHT
)
1985 && (fTypes
& UTZNM_SHORT_STANDARD
) != 0
1986 && (fTypes
& UTZNM_SHORT_DAYLIGHT
) != 0) {
1987 ntype
= UTZNM_SHORT_GENERIC
;
1990 if (fResults
== NULL
) {
1991 fResults
= new TimeZoneNames::MatchInfoCollection();
1992 if (fResults
== NULL
) {
1993 status
= U_MEMORY_ALLOCATION_ERROR
;
1996 if (U_SUCCESS(status
)) {
1997 U_ASSERT(fResults
!= NULL
);
1998 U_ASSERT(match
->mzID
!= NULL
);
1999 fResults
->addMetaZone(ntype
, matchLength
, UnicodeString(match
->mzID
, -1), status
);
2000 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
2001 fMaxMatchLen
= matchLength
;
2009 TimeZoneNames::MatchInfoCollection
*
2010 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
2011 // give the ownership to the caller
2012 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
2013 maxMatchLen
= fMaxMatchLen
;
2023 * Deleter for TZDBNames
2025 static void U_CALLCONV
2026 deleteTZDBNames(void *obj
) {
2028 delete (TZDBNames
*)obj
;
2032 static void U_CALLCONV
initTZDBNamesMap(UErrorCode
&status
) {
2033 gTZDBNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2034 if (U_FAILURE(status
)) {
2035 gTZDBNamesMap
= NULL
;
2038 // no key deleters for tzdb name maps
2039 uhash_setValueDeleter(gTZDBNamesMap
, deleteTZDBNames
);
2040 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2044 * Deleter for TZDBNameInfo
2046 static void U_CALLCONV
2047 deleteTZDBNameInfo(void *obj
) {
2053 static void U_CALLCONV
prepareFind(UErrorCode
&status
) {
2054 if (U_FAILURE(status
)) {
2057 gTZDBNamesTrie
= new TextTrieMap(TRUE
, deleteTZDBNameInfo
);
2058 if (gTZDBNamesTrie
== NULL
) {
2059 status
= U_MEMORY_ALLOCATION_ERROR
;
2063 const UnicodeString
*mzID
;
2064 StringEnumeration
*mzIDs
= TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2065 if (U_SUCCESS(status
)) {
2066 while ((mzID
= mzIDs
->snext(status
)) != 0 && U_SUCCESS(status
)) {
2067 const TZDBNames
*names
= TZDBTimeZoneNames::getMetaZoneNames(*mzID
, status
);
2068 if (U_FAILURE(status
)) {
2071 if (names
== NULL
) {
2074 const UChar
*std
= names
->getName(UTZNM_SHORT_STANDARD
);
2075 const UChar
*dst
= names
->getName(UTZNM_SHORT_DAYLIGHT
);
2076 if (std
== NULL
&& dst
== NULL
) {
2079 int32_t numRegions
= 0;
2080 const char **parseRegions
= names
->getParseRegions(numRegions
);
2082 // The tz database contains a few zones sharing a
2083 // same name for both standard time and daylight saving
2084 // time. For example, Australia/Sydney observes DST,
2085 // but "EST" is used for both standard and daylight.
2086 // we need to store the information for later processing.
2087 UBool ambiguousType
= (std
!= NULL
&& dst
!= NULL
&& u_strcmp(std
, dst
) == 0);
2089 const UChar
*uMzID
= ZoneMeta::findMetaZoneID(*mzID
);
2091 TZDBNameInfo
*stdInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2092 if (stdInf
== NULL
) {
2093 status
= U_MEMORY_ALLOCATION_ERROR
;
2096 stdInf
->mzID
= uMzID
;
2097 stdInf
->type
= UTZNM_SHORT_STANDARD
;
2098 stdInf
->ambiguousType
= ambiguousType
;
2099 stdInf
->parseRegions
= parseRegions
;
2100 stdInf
->nRegions
= numRegions
;
2101 gTZDBNamesTrie
->put(std
, stdInf
, status
);
2103 if (U_SUCCESS(status
) && dst
!= NULL
) {
2104 TZDBNameInfo
*dstInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2105 if (dstInf
== NULL
) {
2106 status
= U_MEMORY_ALLOCATION_ERROR
;
2109 dstInf
->mzID
= uMzID
;
2110 dstInf
->type
= UTZNM_SHORT_DAYLIGHT
;
2111 dstInf
->ambiguousType
= ambiguousType
;
2112 dstInf
->parseRegions
= parseRegions
;
2113 dstInf
->nRegions
= numRegions
;
2114 gTZDBNamesTrie
->put(dst
, dstInf
, status
);
2120 if (U_FAILURE(status
)) {
2121 delete gTZDBNamesTrie
;
2122 gTZDBNamesTrie
= NULL
;
2126 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2131 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale
& locale
)
2133 UBool useWorld
= TRUE
;
2134 const char* region
= fLocale
.getCountry();
2135 int32_t regionLen
= static_cast<int32_t>(uprv_strlen(region
));
2136 if (regionLen
== 0) {
2137 UErrorCode status
= U_ZERO_ERROR
;
2138 char loc
[ULOC_FULLNAME_CAPACITY
];
2139 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
2140 regionLen
= uloc_getCountry(loc
, fRegion
, sizeof(fRegion
), &status
);
2141 if (U_SUCCESS(status
) && regionLen
< (int32_t)sizeof(fRegion
)) {
2144 } else if (regionLen
< (int32_t)sizeof(fRegion
)) {
2145 uprv_strcpy(fRegion
, region
);
2149 uprv_strcpy(fRegion
, "001");
2153 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2157 TZDBTimeZoneNames::operator==(const TimeZoneNames
& other
) const {
2158 if (this == &other
) {
2161 // No implementation for now
2166 TZDBTimeZoneNames::clone() const {
2167 return new TZDBTimeZoneNames(fLocale
);
2171 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
2172 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2176 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
2177 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
2181 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
2182 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
2186 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
2187 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
2191 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString
& mzID
,
2192 UTimeZoneNameType type
,
2193 UnicodeString
& name
) const {
2195 if (mzID
.isEmpty()) {
2199 UErrorCode status
= U_ZERO_ERROR
;
2200 const TZDBNames
*tzdbNames
= TZDBTimeZoneNames::getMetaZoneNames(mzID
, status
);
2201 if (U_SUCCESS(status
)) {
2202 if (tzdbNames
!= NULL
) {
2203 const UChar
*s
= tzdbNames
->getName(type
);
2205 name
.setTo(TRUE
, s
, -1);
2214 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString
& /* tzID */, UTimeZoneNameType
/* type */, UnicodeString
& name
) const {
2215 // No abbreviations associated a zone directly for now.
2220 TZDBTimeZoneNames::MatchInfoCollection
*
2221 TZDBTimeZoneNames::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
2222 umtx_initOnce(gTZDBNamesTrieInitOnce
, &prepareFind
, status
);
2223 if (U_FAILURE(status
)) {
2227 TZDBNameSearchHandler
handler(types
, fRegion
);
2228 gTZDBNamesTrie
->search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
2229 if (U_FAILURE(status
)) {
2233 return handler
.getMatches(maxLen
);
2237 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
2238 umtx_initOnce(gTZDBNamesMapInitOnce
, &initTZDBNamesMap
, status
);
2239 if (U_FAILURE(status
)) {
2243 TZDBNames
* tzdbNames
= NULL
;
2245 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
2246 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
2247 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
2248 mzIDKey
[mzID
.length()] = 0;
2250 static UMutex
*gTZDBNamesMapLock
= STATIC_NEW(UMutex
);
2251 umtx_lock(gTZDBNamesMapLock
);
2253 void *cacheVal
= uhash_get(gTZDBNamesMap
, mzIDKey
);
2254 if (cacheVal
== NULL
) {
2255 UResourceBundle
*zoneStringsRes
= ures_openDirect(U_ICUDATA_ZONE
, "tzdbNames", &status
);
2256 zoneStringsRes
= ures_getByKey(zoneStringsRes
, gZoneStrings
, zoneStringsRes
, &status
);
2257 if (U_SUCCESS(status
)) {
2258 char key
[ZID_KEY_MAX
+ 1];
2259 mergeTimeZoneKey(mzID
, key
);
2260 tzdbNames
= TZDBNames::createInstance(zoneStringsRes
, key
);
2262 if (tzdbNames
== NULL
) {
2263 cacheVal
= (void *)EMPTY
;
2265 cacheVal
= tzdbNames
;
2267 // Use the persistent ID as the resource key, so we can
2268 // avoid duplications.
2269 // TODO: Is there a more efficient way, like intern() in Java?
2270 void* newKey
= (void*) ZoneMeta::findMetaZoneID(mzID
);
2271 if (newKey
!= NULL
) {
2272 uhash_put(gTZDBNamesMap
, newKey
, cacheVal
, &status
);
2273 if (U_FAILURE(status
)) {
2274 if (tzdbNames
!= NULL
) {
2280 // Should never happen with a valid input
2281 if (tzdbNames
!= NULL
) {
2282 // It's not possible that we get a valid tzdbNames with unknown ID.
2283 // But just in case..
2289 ures_close(zoneStringsRes
);
2290 } else if (cacheVal
!= EMPTY
) {
2291 tzdbNames
= (TZDBNames
*)cacheVal
;
2294 umtx_unlock(gTZDBNamesMapLock
);
2302 #endif /* #if !UCONFIG_NO_FORMATTING */