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 gTZDBNamesMapLock
= U_MUTEX_INITIALIZER
;
53 static UMutex gDataMutex
= U_MUTEX_INITIALIZER
;
55 static UHashtable
* gTZDBNamesMap
= NULL
;
56 static icu::UInitOnce gTZDBNamesMapInitOnce
= U_INITONCE_INITIALIZER
;
58 static TextTrieMap
* gTZDBNamesTrie
= NULL
;
59 static icu::UInitOnce gTZDBNamesTrieInitOnce
= U_INITONCE_INITIALIZER
;
61 // The order in which strings are stored may be different than the order in the public enum.
62 enum UTimeZoneNameTypeIndex
{
63 UTZNM_INDEX_UNKNOWN
= -1,
64 UTZNM_INDEX_EXEMPLAR_LOCATION
,
65 UTZNM_INDEX_LONG_GENERIC
,
66 UTZNM_INDEX_LONG_STANDARD
,
67 UTZNM_INDEX_LONG_DAYLIGHT
,
68 UTZNM_INDEX_SHORT_GENERIC
,
69 UTZNM_INDEX_SHORT_STANDARD
,
70 UTZNM_INDEX_SHORT_DAYLIGHT
,
73 static const UChar
* const EMPTY_NAMES
[UTZNM_INDEX_COUNT
] = {0,0,0,0,0,0,0};
76 static UBool U_CALLCONV
tzdbTimeZoneNames_cleanup(void) {
77 if (gTZDBNamesMap
!= NULL
) {
78 uhash_close(gTZDBNamesMap
);
81 gTZDBNamesMapInitOnce
.reset();
83 if (gTZDBNamesTrie
!= NULL
) {
84 delete gTZDBNamesTrie
;
85 gTZDBNamesTrie
= NULL
;
87 gTZDBNamesTrieInitOnce
.reset();
94 * ZNameInfo stores zone name information in the trie
97 UTimeZoneNameType type
;
103 * ZMatchInfo stores zone name match information used by find method
106 const ZNameInfo
* znameInfo
;
111 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
);
113 #define DEFAULT_CHARACTERNODE_CAPACITY 1
115 // ---------------------------------------------------
116 // CharacterNode class implementation
117 // ---------------------------------------------------
118 void CharacterNode::clear() {
119 uprv_memset(this, 0, sizeof(*this));
122 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
123 if (fValues
== NULL
) {
125 } else if (!fHasValuesVector
) {
127 valueDeleter(fValues
);
130 delete (UVector
*)fValues
;
135 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
136 if (U_FAILURE(status
)) {
142 if (fValues
== NULL
) {
145 // At least one value already.
146 if (!fHasValuesVector
) {
147 // There is only one value so far, and not in a vector yet.
148 // Create a vector and add the old value.
149 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
150 if (U_FAILURE(status
)) {
156 values
->addElement(fValues
, status
);
158 fHasValuesVector
= TRUE
;
160 // Add the new value.
161 ((UVector
*)fValues
)->addElement(value
, status
);
165 // ---------------------------------------------------
166 // TextTrieMapSearchResultHandler class implementation
167 // ---------------------------------------------------
168 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
171 // ---------------------------------------------------
172 // TextTrieMap class implementation
173 // ---------------------------------------------------
174 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
175 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
176 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
179 TextTrieMap::~TextTrieMap() {
181 for (index
= 0; index
< fNodesCount
; ++index
) {
182 fNodes
[index
].deleteValues(fValueDeleter
);
185 if (fLazyContents
!= NULL
) {
186 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
188 fValueDeleter(fLazyContents
->elementAt(i
+1));
191 delete fLazyContents
;
195 int32_t TextTrieMap::isEmpty() const {
196 // Use a separate field for fIsEmpty because it will remain unchanged once the
197 // Trie is built, while fNodes and fLazyContents change with the lazy init
198 // of the nodes structure. Trying to test the changing fields has
199 // thread safety complications.
204 // We defer actually building the TextTrieMap node structure until the first time a
205 // search is performed. put() simply saves the parameters in case we do
206 // eventually need to build it.
209 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
210 const UChar
*s
= sp
.get(key
, status
);
211 put(s
, value
, status
);
214 // This method is designed for a persistent key, such as string key stored in
217 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
219 if (fLazyContents
== NULL
) {
220 fLazyContents
= new UVector(status
);
221 if (fLazyContents
== NULL
) {
222 status
= U_MEMORY_ALLOCATION_ERROR
;
225 if (U_FAILURE(status
)) {
227 fValueDeleter((void*) key
);
231 U_ASSERT(fLazyContents
!= NULL
);
233 UChar
*s
= const_cast<UChar
*>(key
);
234 fLazyContents
->addElement(s
, status
);
235 if (U_FAILURE(status
)) {
237 fValueDeleter((void*) key
);
242 fLazyContents
->addElement(value
, status
);
246 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
247 if (fNodes
== NULL
) {
248 fNodesCapacity
= 512;
249 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
250 if (fNodes
== NULL
) {
251 status
= U_MEMORY_ALLOCATION_ERROR
;
254 fNodes
[0].clear(); // Init root node.
258 UnicodeString foldedKey
;
259 const UChar
*keyBuffer
;
262 // Ok to use fastCopyFrom() because we discard the copy when we return.
263 foldedKey
.fastCopyFrom(key
).foldCase();
264 keyBuffer
= foldedKey
.getBuffer();
265 keyLength
= foldedKey
.length();
267 keyBuffer
= key
.getBuffer();
268 keyLength
= key
.length();
271 CharacterNode
*node
= fNodes
;
273 for (index
= 0; index
< keyLength
; ++index
) {
274 node
= addChildNode(node
, keyBuffer
[index
], status
);
276 node
->addValue(value
, fValueDeleter
, status
);
280 TextTrieMap::growNodes() {
281 if (fNodesCapacity
== 0xffff) {
282 return FALSE
; // We use 16-bit node indexes.
284 int32_t newCapacity
= fNodesCapacity
+ 1000;
285 if (newCapacity
> 0xffff) {
286 newCapacity
= 0xffff;
288 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
289 if (newNodes
== NULL
) {
292 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
295 fNodesCapacity
= newCapacity
;
300 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
301 if (U_FAILURE(status
)) {
304 // Linear search of the sorted list of children.
305 uint16_t prevIndex
= 0;
306 uint16_t nodeIndex
= parent
->fFirstChild
;
307 while (nodeIndex
> 0) {
308 CharacterNode
*current
= fNodes
+ nodeIndex
;
309 UChar childCharacter
= current
->fCharacter
;
310 if (childCharacter
== c
) {
312 } else if (childCharacter
> c
) {
315 prevIndex
= nodeIndex
;
316 nodeIndex
= current
->fNextSibling
;
319 // Ensure capacity. Grow fNodes[] if needed.
320 if (fNodesCount
== fNodesCapacity
) {
321 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
323 status
= U_MEMORY_ALLOCATION_ERROR
;
326 parent
= fNodes
+ parentIndex
;
329 // Insert a new child node with c in sorted order.
330 CharacterNode
*node
= fNodes
+ fNodesCount
;
332 node
->fCharacter
= c
;
333 node
->fNextSibling
= nodeIndex
;
334 if (prevIndex
== 0) {
335 parent
->fFirstChild
= (uint16_t)fNodesCount
;
337 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
344 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
345 // Linear search of the sorted list of children.
346 uint16_t nodeIndex
= parent
->fFirstChild
;
347 while (nodeIndex
> 0) {
348 CharacterNode
*current
= fNodes
+ nodeIndex
;
349 UChar childCharacter
= current
->fCharacter
;
350 if (childCharacter
== c
) {
352 } else if (childCharacter
> c
) {
355 nodeIndex
= current
->fNextSibling
;
360 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
361 static UMutex TextTrieMutex
= U_MUTEX_INITIALIZER
;
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.
389 Mutex
lock(&TextTrieMutex
);
390 if (fLazyContents
!= NULL
) {
391 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
392 nonConstThis
->buildTrie(status
);
395 if (fNodes
== NULL
) {
398 search(fNodes
, text
, start
, start
, handler
, status
);
402 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
403 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
404 if (U_FAILURE(status
)) {
407 if (node
->hasValues()) {
408 if (!handler
->handleMatch(index
- start
, node
, status
)) {
411 if (U_FAILURE(status
)) {
416 // for folding we need to get a complete code point.
417 // size of character may grow after fold operation;
418 // then we need to get result as UTF16 code units.
419 UChar32 c32
= text
.char32At(index
);
420 index
+= U16_LENGTH(c32
);
421 UnicodeString
tmp(c32
);
424 while (tmpidx
< tmp
.length()) {
425 UChar c
= tmp
.charAt(tmpidx
++);
426 node
= getChildNode(node
, c
);
432 // here we just get the next UTF16 code unit
433 UChar c
= text
.charAt(index
++);
434 node
= getChildNode(node
, c
);
437 search(node
, text
, start
, index
, handler
, status
);
441 // ---------------------------------------------------
442 // ZNStringPool class implementation
443 // ---------------------------------------------------
444 static const int32_t POOL_CHUNK_SIZE
= 2000;
445 struct ZNStringPoolChunk
: public UMemory
{
446 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
447 int32_t fLimit
; // Index to start of unused area at end of fStrings
448 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
452 ZNStringPoolChunk::ZNStringPoolChunk() {
457 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
460 if (U_FAILURE(status
)) {
463 fChunks
= new ZNStringPoolChunk
;
464 if (fChunks
== NULL
) {
465 status
= U_MEMORY_ALLOCATION_ERROR
;
469 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
470 uhash_compareUChars
/* keyComp */,
471 uhash_compareUChars
/* valueComp */,
473 if (U_FAILURE(status
)) {
478 ZNStringPool::~ZNStringPool() {
484 while (fChunks
!= NULL
) {
485 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
491 static const UChar EmptyString
= 0;
493 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
494 const UChar
*pooledString
;
495 if (U_FAILURE(status
)) {
499 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
500 if (pooledString
!= NULL
) {
504 int32_t length
= u_strlen(s
);
505 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
506 if (remainingLength
<= length
) {
507 U_ASSERT(length
< POOL_CHUNK_SIZE
);
508 if (length
>= POOL_CHUNK_SIZE
) {
509 status
= U_INTERNAL_PROGRAM_ERROR
;
512 ZNStringPoolChunk
*oldChunk
= fChunks
;
513 fChunks
= new ZNStringPoolChunk
;
514 if (fChunks
== NULL
) {
515 status
= U_MEMORY_ALLOCATION_ERROR
;
518 fChunks
->fNext
= oldChunk
;
521 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
522 u_strcpy(destString
, s
);
523 fChunks
->fLimit
+= (length
+ 1);
524 uhash_put(fHash
, destString
, destString
, &status
);
530 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
531 // into the pool's storage. Used for strings from resource bundles,
532 // which will perisist for the life of the zone string formatter, and
533 // therefore can be used directly without copying.
534 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
535 const UChar
*pooledString
;
536 if (U_FAILURE(status
)) {
540 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
541 if (pooledString
== NULL
) {
542 UChar
*ncs
= const_cast<UChar
*>(s
);
543 uhash_put(fHash
, ncs
, ncs
, &status
);
550 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
551 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
552 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
556 * freeze(). Close the hash table that maps to the pooled strings.
557 * After freezing, the pool can not be searched or added to,
558 * but all existing references to pooled strings remain valid.
560 * The main purpose is to recover the storage used for the hash.
562 void ZNStringPool::freeze() {
569 * This class stores name data for a meta zone or time zone.
571 class ZNames
: public UMemory
{
573 friend class TimeZoneNamesImpl
;
575 static UTimeZoneNameTypeIndex
getTZNameTypeIndex(UTimeZoneNameType type
) {
577 case UTZNM_EXEMPLAR_LOCATION
: return UTZNM_INDEX_EXEMPLAR_LOCATION
;
578 case UTZNM_LONG_GENERIC
: return UTZNM_INDEX_LONG_GENERIC
;
579 case UTZNM_LONG_STANDARD
: return UTZNM_INDEX_LONG_STANDARD
;
580 case UTZNM_LONG_DAYLIGHT
: return UTZNM_INDEX_LONG_DAYLIGHT
;
581 case UTZNM_SHORT_GENERIC
: return UTZNM_INDEX_SHORT_GENERIC
;
582 case UTZNM_SHORT_STANDARD
: return UTZNM_INDEX_SHORT_STANDARD
;
583 case UTZNM_SHORT_DAYLIGHT
: return UTZNM_INDEX_SHORT_DAYLIGHT
;
584 default: return UTZNM_INDEX_UNKNOWN
;
587 static UTimeZoneNameType
getTZNameType(UTimeZoneNameTypeIndex index
) {
589 case UTZNM_INDEX_EXEMPLAR_LOCATION
: return UTZNM_EXEMPLAR_LOCATION
;
590 case UTZNM_INDEX_LONG_GENERIC
: return UTZNM_LONG_GENERIC
;
591 case UTZNM_INDEX_LONG_STANDARD
: return UTZNM_LONG_STANDARD
;
592 case UTZNM_INDEX_LONG_DAYLIGHT
: return UTZNM_LONG_DAYLIGHT
;
593 case UTZNM_INDEX_SHORT_GENERIC
: return UTZNM_SHORT_GENERIC
;
594 case UTZNM_INDEX_SHORT_STANDARD
: return UTZNM_SHORT_STANDARD
;
595 case UTZNM_INDEX_SHORT_DAYLIGHT
: return UTZNM_SHORT_DAYLIGHT
;
596 default: return UTZNM_UNKNOWN
;
600 const UChar
* fNames
[UTZNM_INDEX_COUNT
];
601 UBool fDidAddIntoTrie
;
603 // Whether we own the location string, if computed rather than loaded from a bundle.
604 // A meta zone names instance never has an exemplar location string.
605 UBool fOwnsLocationName
;
607 ZNames(const UChar
* names
[], const UChar
* locationName
)
608 : fDidAddIntoTrie(FALSE
) {
609 uprv_memcpy(fNames
, names
, sizeof(fNames
));
610 if (locationName
!= NULL
) {
611 fOwnsLocationName
= TRUE
;
612 fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
] = locationName
;
614 fOwnsLocationName
= FALSE
;
620 if (fOwnsLocationName
) {
621 const UChar
* locationName
= fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
];
622 U_ASSERT(locationName
!= NULL
);
623 uprv_free((void*) locationName
);
628 static void* createMetaZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
629 const UnicodeString
& mzID
, UErrorCode
& status
) {
630 if (U_FAILURE(status
)) { return NULL
; }
631 U_ASSERT(names
!= NULL
);
633 // Use the persistent ID as the resource key, so we can
634 // avoid duplications.
635 // TODO: Is there a more efficient way, like intern() in Java?
636 void* key
= (void*) ZoneMeta::findMetaZoneID(mzID
);
638 if (uprv_memcmp(names
, EMPTY_NAMES
, sizeof(EMPTY_NAMES
)) == 0) {
639 value
= (void*) EMPTY
;
641 value
= (void*) (new ZNames(names
, NULL
));
643 status
= U_MEMORY_ALLOCATION_ERROR
;
647 uhash_put(cache
, key
, value
, &status
);
651 static void* createTimeZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
652 const UnicodeString
& tzID
, UErrorCode
& status
) {
653 if (U_FAILURE(status
)) { return NULL
; }
654 U_ASSERT(names
!= NULL
);
656 // If necessary, compute the location name from the time zone name.
657 UChar
* locationName
= NULL
;
658 if (names
[UTZNM_INDEX_EXEMPLAR_LOCATION
] == NULL
) {
659 UnicodeString locationNameUniStr
;
660 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, locationNameUniStr
);
662 // Copy the computed location name to the heap
663 if (locationNameUniStr
.length() > 0) {
664 const UChar
* buff
= locationNameUniStr
.getTerminatedBuffer();
665 int32_t len
= sizeof(UChar
) * (locationNameUniStr
.length() + 1);
666 locationName
= (UChar
*) uprv_malloc(len
);
667 if (locationName
== NULL
) {
668 status
= U_MEMORY_ALLOCATION_ERROR
;
671 uprv_memcpy(locationName
, buff
, len
);
675 // Use the persistent ID as the resource key, so we can
676 // avoid duplications.
677 // TODO: Is there a more efficient way, like intern() in Java?
678 void* key
= (void*) ZoneMeta::findTimeZoneID(tzID
);
679 void* value
= (void*) (new ZNames(names
, locationName
));
681 status
= U_MEMORY_ALLOCATION_ERROR
;
684 uhash_put(cache
, key
, value
, &status
);
688 const UChar
* getName(UTimeZoneNameType type
) const {
689 UTimeZoneNameTypeIndex index
= getTZNameTypeIndex(type
);
690 return index
>= 0 ? fNames
[index
] : NULL
;
693 void addAsMetaZoneIntoTrie(const UChar
* mzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
694 addNamesIntoTrie(mzID
, NULL
, trie
, status
);
696 void addAsTimeZoneIntoTrie(const UChar
* tzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
697 addNamesIntoTrie(NULL
, tzID
, trie
, status
);
700 void addNamesIntoTrie(const UChar
* mzID
, const UChar
* tzID
, TextTrieMap
& trie
,
701 UErrorCode
& status
) {
702 if (U_FAILURE(status
)) { return; }
703 if (fDidAddIntoTrie
) { return; }
704 fDidAddIntoTrie
= TRUE
;
706 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; i
++) {
707 const UChar
* name
= fNames
[i
];
709 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
710 if (nameinfo
== NULL
) {
711 status
= U_MEMORY_ALLOCATION_ERROR
;
714 nameinfo
->mzID
= mzID
;
715 nameinfo
->tzID
= tzID
;
716 nameinfo
->type
= getTZNameType((UTimeZoneNameTypeIndex
)i
);
717 trie
.put(name
, nameinfo
, status
); // trie.put() takes ownership of the key
718 if (U_FAILURE(status
)) {
729 struct ZNames::ZNamesLoader
: public ResourceSink
{
730 const UChar
*names
[UTZNM_INDEX_COUNT
];
735 virtual ~ZNamesLoader();
737 /** Reset for loading another set of names. */
739 uprv_memcpy(names
, EMPTY_NAMES
, sizeof(names
));
742 void loadMetaZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& mzID
, UErrorCode
& errorCode
) {
743 if (U_FAILURE(errorCode
)) { return; }
745 char key
[ZID_KEY_MAX
+ 1];
746 mergeTimeZoneKey(mzID
, key
);
748 loadNames(zoneStrings
, key
, errorCode
);
751 void loadTimeZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& tzID
, UErrorCode
& errorCode
) {
752 // Replace "/" with ":".
753 UnicodeString
uKey(tzID
);
754 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
755 if (uKey
.charAt(i
) == (UChar
)0x2F) {
756 uKey
.setCharAt(i
, (UChar
)0x3A);
760 char key
[ZID_KEY_MAX
+ 1];
761 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
763 loadNames(zoneStrings
, key
, errorCode
);
766 void loadNames(const UResourceBundle
* zoneStrings
, const char* key
, UErrorCode
& errorCode
) {
767 U_ASSERT(zoneStrings
!= NULL
);
768 U_ASSERT(key
!= NULL
);
769 U_ASSERT(key
[0] != '\0');
771 UErrorCode localStatus
= U_ZERO_ERROR
;
773 ures_getAllItemsWithFallback(zoneStrings
, key
, *this, localStatus
);
775 // Ignore errors, but propogate possible warnings.
776 if (U_SUCCESS(localStatus
)) {
777 errorCode
= localStatus
;
781 void setNameIfEmpty(const char* key
, const ResourceValue
* value
, UErrorCode
& errorCode
) {
782 UTimeZoneNameTypeIndex type
= nameTypeFromKey(key
);
783 if (type
== UTZNM_INDEX_UNKNOWN
) { return; }
784 if (names
[type
] == NULL
) {
786 // 'NO_NAME' indicates internally that this field should remain empty. It will be
787 // replaced by 'NULL' in getNames()
788 names
[type
] = (value
== NULL
) ? NO_NAME
: value
->getString(length
, errorCode
);
792 virtual void put(const char* key
, ResourceValue
& value
, UBool
/*noFallback*/,
793 UErrorCode
&errorCode
) {
794 ResourceTable namesTable
= value
.getTable(errorCode
);
795 if (U_FAILURE(errorCode
)) { return; }
796 for (int32_t i
= 0; namesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
797 if (value
.isNoInheritanceMarker()) {
798 setNameIfEmpty(key
, NULL
, errorCode
);
800 setNameIfEmpty(key
, &value
, errorCode
);
805 static UTimeZoneNameTypeIndex
nameTypeFromKey(const char *key
) {
807 if ((c0
= key
[0]) == 0 || (c1
= key
[1]) == 0 || key
[2] != 0) {
808 return UTZNM_INDEX_UNKNOWN
;
811 return c1
== 'g' ? UTZNM_INDEX_LONG_GENERIC
:
812 c1
== 's' ? UTZNM_INDEX_LONG_STANDARD
:
813 c1
== 'd' ? UTZNM_INDEX_LONG_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
814 } else if (c0
== 's') {
815 return c1
== 'g' ? UTZNM_INDEX_SHORT_GENERIC
:
816 c1
== 's' ? UTZNM_INDEX_SHORT_STANDARD
:
817 c1
== 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
818 } else if (c0
== 'e' && c1
== 'c') {
819 return UTZNM_INDEX_EXEMPLAR_LOCATION
;
821 return UTZNM_INDEX_UNKNOWN
;
825 * Returns an array of names. It is the caller's responsibility to copy the data into a
826 * permanent location, as the returned array is owned by the loader instance and may be
827 * cleared or leave scope.
829 * This is different than Java, where the array will no longer be modified and null
832 const UChar
** getNames() {
833 // Remove 'NO_NAME' references in the array and replace with 'NULL'
834 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; ++i
) {
835 if (names
[i
] == NO_NAME
) {
843 ZNames::ZNamesLoader::~ZNamesLoader() {}
846 // ---------------------------------------------------
847 // The meta zone ID enumeration class
848 // ---------------------------------------------------
849 class MetaZoneIDsEnumeration
: public StringEnumeration
{
851 MetaZoneIDsEnumeration();
852 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
853 MetaZoneIDsEnumeration(UVector
* mzIDs
);
854 virtual ~MetaZoneIDsEnumeration();
855 static UClassID U_EXPORT2
getStaticClassID(void);
856 virtual UClassID
getDynamicClassID(void) const;
857 virtual const UnicodeString
* snext(UErrorCode
& status
);
858 virtual void reset(UErrorCode
& status
);
859 virtual int32_t count(UErrorCode
& status
) const;
863 const UVector
* fMetaZoneIDs
;
864 UVector
*fLocalVector
;
867 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
869 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
870 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
873 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
874 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
875 fLen
= fMetaZoneIDs
->size();
878 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
879 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
881 fLen
= fMetaZoneIDs
->size();
886 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
887 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
888 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
895 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
900 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
904 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
911 // ---------------------------------------------------
912 // ZNameSearchHandler
913 // ---------------------------------------------------
914 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
916 ZNameSearchHandler(uint32_t types
);
917 virtual ~ZNameSearchHandler();
919 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
920 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
924 int32_t fMaxMatchLen
;
925 TimeZoneNames::MatchInfoCollection
* fResults
;
928 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
929 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
932 ZNameSearchHandler::~ZNameSearchHandler() {
933 if (fResults
!= NULL
) {
939 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
940 if (U_FAILURE(status
)) {
943 if (node
->hasValues()) {
944 int32_t valuesCount
= node
->countValues();
945 for (int32_t i
= 0; i
< valuesCount
; i
++) {
946 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
947 if (nameinfo
== NULL
) {
950 if ((nameinfo
->type
& fTypes
) != 0) {
951 // matches a requested type
952 if (fResults
== NULL
) {
953 fResults
= new TimeZoneNames::MatchInfoCollection();
954 if (fResults
== NULL
) {
955 status
= U_MEMORY_ALLOCATION_ERROR
;
958 if (U_SUCCESS(status
)) {
959 U_ASSERT(fResults
!= NULL
);
960 if (nameinfo
->tzID
) {
961 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
963 U_ASSERT(nameinfo
->mzID
);
964 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
966 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
967 fMaxMatchLen
= matchLength
;
976 TimeZoneNames::MatchInfoCollection
*
977 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
978 // give the ownership to the caller
979 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
980 maxMatchLen
= fMaxMatchLen
;
988 // ---------------------------------------------------
991 // TimeZoneNames implementation class. This is the main
992 // part of this module.
993 // ---------------------------------------------------
999 static void U_CALLCONV
1000 deleteZNames(void *obj
) {
1002 delete (ZNames
*) obj
;
1007 * Deleter for ZNameInfo
1009 static void U_CALLCONV
1010 deleteZNameInfo(void *obj
) {
1016 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
1021 fNamesTrieFullyLoaded(FALSE
),
1022 fNamesFullyLoaded(FALSE
),
1023 fNamesTrie(TRUE
, deleteZNameInfo
) {
1024 initialize(locale
, status
);
1028 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
1029 if (U_FAILURE(status
)) {
1033 // Load zoneStrings bundle
1034 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
1035 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
1036 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
1037 if (U_FAILURE(tmpsts
)) {
1043 // Initialize hashtables holding time zone/meta zone names
1044 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1045 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1046 if (U_FAILURE(status
)) {
1051 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
1052 uhash_setValueDeleter(fTZNamesMap
, deleteZNames
);
1053 // no key deleters for name maps
1055 // preload zone strings for the default zone
1056 TimeZone
*tz
= TimeZone::createDefault();
1057 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
1059 loadStrings(UnicodeString(tzID
), status
);
1067 * This method updates the cache and must be called with a lock,
1068 * except initializer.
1071 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
, UErrorCode
& status
) {
1072 loadTimeZoneNames(tzCanonicalID
, status
);
1073 LocalPointer
<StringEnumeration
> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID
, status
));
1074 if (U_FAILURE(status
)) { return; }
1075 U_ASSERT(!mzIDs
.isNull());
1077 const UnicodeString
*mzID
;
1078 while (((mzID
= mzIDs
->snext(status
)) != NULL
) && U_SUCCESS(status
)) {
1079 loadMetaZoneNames(*mzID
, status
);
1083 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1088 TimeZoneNamesImpl::cleanup() {
1089 if (fZoneStrings
!= NULL
) {
1090 ures_close(fZoneStrings
);
1091 fZoneStrings
= NULL
;
1093 if (fMZNamesMap
!= NULL
) {
1094 uhash_close(fMZNamesMap
);
1097 if (fTZNamesMap
!= NULL
) {
1098 uhash_close(fTZNamesMap
);
1104 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
1105 if (this == &other
) {
1108 // No implementation for now
1113 TimeZoneNamesImpl::clone() const {
1114 UErrorCode status
= U_ZERO_ERROR
;
1115 return new TimeZoneNamesImpl(fLocale
, status
);
1119 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
1120 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1123 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1125 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode
& status
) {
1126 if (U_FAILURE(status
)) {
1129 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
1130 if (mzIDs
== NULL
) {
1131 return new MetaZoneIDsEnumeration();
1133 return new MetaZoneIDsEnumeration(*mzIDs
);
1137 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1138 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
1141 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1143 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) {
1144 if (U_FAILURE(status
)) {
1147 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1148 if (mappings
== NULL
) {
1149 return new MetaZoneIDsEnumeration();
1152 MetaZoneIDsEnumeration
*senum
= NULL
;
1153 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1154 if (mzIDs
== NULL
) {
1155 status
= U_MEMORY_ALLOCATION_ERROR
;
1157 if (U_SUCCESS(status
)) {
1158 U_ASSERT(mzIDs
!= NULL
);
1159 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1161 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1162 const UChar
*mzID
= map
->mzid
;
1163 if (!mzIDs
->contains((void *)mzID
)) {
1164 mzIDs
->addElement((void *)mzID
, status
);
1167 if (U_SUCCESS(status
)) {
1168 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1177 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1178 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
1181 // static implementation of getMetaZoneID
1183 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) {
1184 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1189 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1190 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
1193 // static implementaion of getReferenceZoneID
1195 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) {
1196 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1201 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1202 UTimeZoneNameType type
,
1203 UnicodeString
& name
) const {
1204 name
.setToBogus(); // cleanup result.
1205 if (mzID
.isEmpty()) {
1209 ZNames
*znames
= NULL
;
1210 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1213 Mutex
lock(&gDataMutex
);
1214 UErrorCode status
= U_ZERO_ERROR
;
1215 znames
= nonConstThis
->loadMetaZoneNames(mzID
, status
);
1216 if (U_FAILURE(status
)) { return name
; }
1219 if (znames
!= NULL
) {
1220 const UChar
* s
= znames
->getName(type
);
1222 name
.setTo(TRUE
, s
, -1);
1229 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1230 name
.setToBogus(); // cleanup result.
1231 if (tzID
.isEmpty()) {
1235 ZNames
*tznames
= NULL
;
1236 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1239 Mutex
lock(&gDataMutex
);
1240 UErrorCode status
= U_ZERO_ERROR
;
1241 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1242 if (U_FAILURE(status
)) { return name
; }
1245 if (tznames
!= NULL
) {
1246 const UChar
*s
= tznames
->getName(type
);
1248 name
.setTo(TRUE
, s
, -1);
1255 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1256 name
.setToBogus(); // cleanup result.
1257 const UChar
* locName
= NULL
;
1258 ZNames
*tznames
= NULL
;
1259 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1262 Mutex
lock(&gDataMutex
);
1263 UErrorCode status
= U_ZERO_ERROR
;
1264 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1265 if (U_FAILURE(status
)) { return name
; }
1268 if (tznames
!= NULL
) {
1269 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1271 if (locName
!= NULL
) {
1272 name
.setTo(TRUE
, locName
, -1);
1279 // Merge the MZ_PREFIX and mzId
1280 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1281 if (mzID
.isEmpty()) {
1286 char mzIdChar
[ZID_KEY_MAX
+ 1];
1288 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1289 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1290 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1291 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1292 result
[keyLen
+ prefixLen
] = '\0';
1296 * This method updates the cache and must be called with a lock
1299 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
1300 if (U_FAILURE(status
)) { return NULL
; }
1301 U_ASSERT(mzID
.length() <= ZID_KEY_MAX
- MZ_PREFIX_LEN
);
1303 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1304 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1305 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1306 mzIDKey
[mzID
.length()] = 0;
1308 void* mznames
= uhash_get(fMZNamesMap
, mzIDKey
);
1309 if (mznames
== NULL
) {
1310 ZNames::ZNamesLoader loader
;
1311 loader
.loadMetaZone(fZoneStrings
, mzID
, status
);
1312 mznames
= ZNames::createMetaZoneAndPutInCache(fMZNamesMap
, loader
.getNames(), mzID
, status
);
1313 if (U_FAILURE(status
)) { return NULL
; }
1316 if (mznames
!= EMPTY
) {
1317 return (ZNames
*)mznames
;
1324 * This method updates the cache and must be called with a lock
1327 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
, UErrorCode
& status
) {
1328 if (U_FAILURE(status
)) { return NULL
; }
1329 U_ASSERT(tzID
.length() <= ZID_KEY_MAX
);
1331 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1332 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1333 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1334 tzIDKey
[tzIDKeyLen
] = 0;
1336 void *tznames
= uhash_get(fTZNamesMap
, tzIDKey
);
1337 if (tznames
== NULL
) {
1338 ZNames::ZNamesLoader loader
;
1339 loader
.loadTimeZone(fZoneStrings
, tzID
, status
);
1340 tznames
= ZNames::createTimeZoneAndPutInCache(fTZNamesMap
, loader
.getNames(), tzID
, status
);
1341 if (U_FAILURE(status
)) { return NULL
; }
1344 // tznames is never EMPTY
1345 return (ZNames
*)tznames
;
1348 TimeZoneNames::MatchInfoCollection
*
1349 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1350 ZNameSearchHandler
handler(types
);
1351 TimeZoneNames::MatchInfoCollection
* matches
;
1352 TimeZoneNamesImpl
* nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1354 // Synchronize so that data is not loaded multiple times.
1355 // TODO: Consider more fine-grained synchronization.
1357 Mutex
lock(&gDataMutex
);
1359 // First try of lookup.
1360 matches
= doFind(handler
, text
, start
, status
);
1361 if (U_FAILURE(status
)) { return NULL
; }
1362 if (matches
!= NULL
) {
1366 // All names are not yet loaded into the trie.
1367 // We may have loaded names for formatting several time zones,
1368 // and might be parsing one of those.
1369 // Populate the parsing trie from all of the already-loaded names.
1370 nonConstThis
->addAllNamesIntoTrie(status
);
1372 // Second try of lookup.
1373 matches
= doFind(handler
, text
, start
, status
);
1374 if (U_FAILURE(status
)) { return NULL
; }
1375 if (matches
!= NULL
) {
1379 // There are still some names we haven't loaded into the trie yet.
1380 // Load everything now.
1381 nonConstThis
->internalLoadAllDisplayNames(status
);
1382 nonConstThis
->addAllNamesIntoTrie(status
);
1383 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1384 if (U_FAILURE(status
)) { return NULL
; }
1386 // Third try: we must return this one.
1387 return doFind(handler
, text
, start
, status
);
1391 TimeZoneNames::MatchInfoCollection
*
1392 TimeZoneNamesImpl::doFind(ZNameSearchHandler
& handler
,
1393 const UnicodeString
& text
, int32_t start
, UErrorCode
& status
) const {
1395 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1396 if (U_FAILURE(status
)) { return NULL
; }
1399 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1400 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1401 // perfect match, or no more names available
1408 // Caller must synchronize.
1409 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode
& status
) {
1410 if (U_FAILURE(status
)) return;
1412 const UHashElement
* element
;
1415 while ((element
= uhash_nextElement(fMZNamesMap
, &pos
)) != NULL
) {
1416 if (element
->value
.pointer
== EMPTY
) { continue; }
1417 UChar
* mzID
= (UChar
*) element
->key
.pointer
;
1418 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1419 znames
->addAsMetaZoneIntoTrie(mzID
, fNamesTrie
, status
);
1420 if (U_FAILURE(status
)) { return; }
1424 while ((element
= uhash_nextElement(fTZNamesMap
, &pos
)) != NULL
) {
1425 if (element
->value
.pointer
== EMPTY
) { continue; }
1426 UChar
* tzID
= (UChar
*) element
->key
.pointer
;
1427 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1428 znames
->addAsTimeZoneIntoTrie(tzID
, fNamesTrie
, status
);
1429 if (U_FAILURE(status
)) { return; }
1434 static void U_CALLCONV
1435 deleteZNamesLoader(void* obj
) {
1436 if (obj
== DUMMY_LOADER
) { return; }
1437 const ZNames::ZNamesLoader
* loader
= (const ZNames::ZNamesLoader
*) obj
;
1442 struct TimeZoneNamesImpl::ZoneStringsLoader
: public ResourceSink
{
1443 TimeZoneNamesImpl
& tzn
;
1444 UHashtable
* keyToLoader
;
1446 ZoneStringsLoader(TimeZoneNamesImpl
& _tzn
, UErrorCode
& status
)
1448 keyToLoader
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
1449 if (U_FAILURE(status
)) { return; }
1450 uhash_setKeyDeleter(keyToLoader
, uprv_free
);
1451 uhash_setValueDeleter(keyToLoader
, deleteZNamesLoader
);
1453 virtual ~ZoneStringsLoader();
1455 void* createKey(const char* key
, UErrorCode
& status
) {
1456 int32_t len
= sizeof(char) * (uprv_strlen(key
) + 1);
1457 char* newKey
= (char*) uprv_malloc(len
);
1458 if (newKey
== NULL
) {
1459 status
= U_MEMORY_ALLOCATION_ERROR
;
1462 uprv_memcpy(newKey
, key
, len
);
1463 newKey
[len
-1] = '\0';
1464 return (void*) newKey
;
1467 UBool
isMetaZone(const char* key
) {
1468 return (uprv_strlen(key
) >= MZ_PREFIX_LEN
&& uprv_memcmp(key
, gMZPrefix
, MZ_PREFIX_LEN
) == 0);
1471 UnicodeString
mzIDFromKey(const char* key
) {
1472 return UnicodeString(key
+ MZ_PREFIX_LEN
, uprv_strlen(key
) - MZ_PREFIX_LEN
, US_INV
);
1475 UnicodeString
tzIDFromKey(const char* key
) {
1476 UnicodeString
tzID(key
, -1, US_INV
);
1477 // Replace all colons ':' with slashes '/'
1478 for (int i
=0; i
<tzID
.length(); i
++) {
1479 if (tzID
.charAt(i
) == 0x003A) {
1480 tzID
.setCharAt(i
, 0x002F);
1486 void load(UErrorCode
& status
) {
1487 ures_getAllItemsWithFallback(tzn
.fZoneStrings
, "", *this, status
);
1488 if (U_FAILURE(status
)) { return; }
1490 int32_t pos
= UHASH_FIRST
;
1491 const UHashElement
* element
;
1492 while ((element
= uhash_nextElement(keyToLoader
, &pos
)) != NULL
) {
1493 if (element
->value
.pointer
== DUMMY_LOADER
) { continue; }
1494 ZNames::ZNamesLoader
* loader
= (ZNames::ZNamesLoader
*) element
->value
.pointer
;
1495 char* key
= (char*) element
->key
.pointer
;
1497 if (isMetaZone(key
)) {
1498 UnicodeString mzID
= mzIDFromKey(key
);
1499 ZNames::createMetaZoneAndPutInCache(tzn
.fMZNamesMap
, loader
->getNames(), mzID
, status
);
1501 UnicodeString tzID
= tzIDFromKey(key
);
1502 ZNames::createTimeZoneAndPutInCache(tzn
.fTZNamesMap
, loader
->getNames(), tzID
, status
);
1504 if (U_FAILURE(status
)) { return; }
1508 void consumeNamesTable(const char *key
, ResourceValue
&value
, UBool noFallback
,
1509 UErrorCode
&status
) {
1510 if (U_FAILURE(status
)) { return; }
1512 void* loader
= uhash_get(keyToLoader
, key
);
1513 if (loader
== NULL
) {
1514 if (isMetaZone(key
)) {
1515 UnicodeString mzID
= mzIDFromKey(key
);
1516 void* cacheVal
= uhash_get(tzn
.fMZNamesMap
, mzID
.getTerminatedBuffer());
1517 if (cacheVal
!= NULL
) {
1518 // We have already loaded the names for this meta zone.
1519 loader
= (void*) DUMMY_LOADER
;
1521 loader
= (void*) new ZNames::ZNamesLoader();
1522 if (loader
== NULL
) {
1523 status
= U_MEMORY_ALLOCATION_ERROR
;
1528 UnicodeString tzID
= tzIDFromKey(key
);
1529 void* cacheVal
= uhash_get(tzn
.fTZNamesMap
, tzID
.getTerminatedBuffer());
1530 if (cacheVal
!= NULL
) {
1531 // We have already loaded the names for this time zone.
1532 loader
= (void*) DUMMY_LOADER
;
1534 loader
= (void*) new ZNames::ZNamesLoader();
1535 if (loader
== NULL
) {
1536 status
= U_MEMORY_ALLOCATION_ERROR
;
1542 void* newKey
= createKey(key
, status
);
1543 if (U_FAILURE(status
)) {
1544 deleteZNamesLoader(loader
);
1548 uhash_put(keyToLoader
, newKey
, loader
, &status
);
1549 if (U_FAILURE(status
)) { return; }
1552 if (loader
!= DUMMY_LOADER
) {
1553 // Let the ZNamesLoader consume the names table.
1554 ((ZNames::ZNamesLoader
*)loader
)->put(key
, value
, noFallback
, status
);
1558 virtual void put(const char *key
, ResourceValue
&value
, UBool noFallback
,
1559 UErrorCode
&status
) {
1560 ResourceTable timeZonesTable
= value
.getTable(status
);
1561 if (U_FAILURE(status
)) { return; }
1562 for (int32_t i
= 0; timeZonesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
1563 U_ASSERT(!value
.isNoInheritanceMarker());
1564 if (value
.getType() == URES_TABLE
) {
1565 consumeNamesTable(key
, value
, noFallback
, status
);
1567 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1568 // All time zone fields are tables.
1570 if (U_FAILURE(status
)) { return; }
1575 // Virtual destructors must be defined out of line.
1576 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1577 uhash_close(keyToLoader
);
1580 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode
& status
) {
1581 if (U_FAILURE(status
)) return;
1584 Mutex
lock(&gDataMutex
);
1585 internalLoadAllDisplayNames(status
);
1589 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString
& tzID
,
1590 const UTimeZoneNameType types
[], int32_t numTypes
,
1591 UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
1592 if (U_FAILURE(status
)) return;
1594 if (tzID
.isEmpty()) { return; }
1595 void* tznames
= NULL
;
1596 void* mznames
= NULL
;
1597 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1599 // Load the time zone strings
1601 Mutex
lock(&gDataMutex
);
1602 tznames
= (void*) nonConstThis
->loadTimeZoneNames(tzID
, status
);
1603 if (U_FAILURE(status
)) { return; }
1605 U_ASSERT(tznames
!= NULL
);
1607 // Load the values into the dest array
1608 for (int i
= 0; i
< numTypes
; i
++) {
1609 UTimeZoneNameType type
= types
[i
];
1610 const UChar
* name
= ((ZNames
*)tznames
)->getName(type
);
1612 if (mznames
== NULL
) {
1613 // Load the meta zone name
1615 getMetaZoneID(tzID
, date
, mzID
);
1616 if (mzID
.isEmpty()) {
1617 mznames
= (void*) EMPTY
;
1619 // Load the meta zone strings
1620 // Mutex is scoped to the "else" statement
1621 Mutex
lock(&gDataMutex
);
1622 mznames
= (void*) nonConstThis
->loadMetaZoneNames(mzID
, status
);
1623 if (U_FAILURE(status
)) { return; }
1624 // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1625 // a dummy object instead of NULL.
1626 if (mznames
== NULL
) {
1627 mznames
= (void*) EMPTY
;
1631 U_ASSERT(mznames
!= NULL
);
1632 if (mznames
!= EMPTY
) {
1633 name
= ((ZNames
*)mznames
)->getName(type
);
1637 dest
[i
].setTo(TRUE
, name
, -1);
1639 dest
[i
].setToBogus();
1644 // Caller must synchronize.
1645 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode
& status
) {
1646 if (!fNamesFullyLoaded
) {
1647 fNamesFullyLoaded
= TRUE
;
1649 ZoneStringsLoader
loader(*this, status
);
1650 loader
.load(status
);
1651 if (U_FAILURE(status
)) { return; }
1653 const UnicodeString
*id
;
1655 // load strings for all zones
1656 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(
1657 UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1658 if (U_SUCCESS(status
)) {
1659 while ((id
= tzIDs
->snext(status
)) != NULL
) {
1660 if (U_FAILURE(status
)) {
1663 UnicodeString
copy(*id
);
1664 void* value
= uhash_get(fTZNamesMap
, copy
.getTerminatedBuffer());
1665 if (value
== NULL
) {
1666 // loadStrings also loads related metazone strings
1667 loadStrings(*id
, status
);
1671 if (tzIDs
!= NULL
) {
1679 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1680 static const int32_t gEtcPrefixLen
= 4;
1681 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1682 static const int32_t gSystemVPrefixLen
= 8;
1683 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1684 static const int32_t gRiyadh8Len
= 7;
1686 UnicodeString
& U_EXPORT2
1687 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1688 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1689 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1694 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1695 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1696 name
.setTo(tzID
, sep
+ 1);
1697 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1698 UnicodeString((UChar
)0x20 /* space */));
1705 // ---------------------------------------------------
1706 // TZDBTimeZoneNames and its supporting classes
1708 // TZDBTimeZoneNames is an implementation class of
1709 // TimeZoneNames holding the IANA tz database abbreviations.
1710 // ---------------------------------------------------
1712 class TZDBNames
: public UMemory
{
1714 virtual ~TZDBNames();
1716 static TZDBNames
* createInstance(UResourceBundle
* rb
, const char* key
);
1717 const UChar
* getName(UTimeZoneNameType type
) const;
1718 const char** getParseRegions(int32_t& numRegions
) const;
1721 TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
);
1724 const UChar
** fNames
;
1726 int32_t fNumRegions
;
1729 TZDBNames::TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
)
1732 fNumRegions(numRegions
) {
1735 TZDBNames::~TZDBNames() {
1736 if (fNames
!= NULL
) {
1739 if (fRegions
!= NULL
) {
1740 char **p
= fRegions
;
1741 for (int32_t i
= 0; i
< fNumRegions
; p
++, i
++) {
1744 uprv_free(fRegions
);
1749 TZDBNames::createInstance(UResourceBundle
* rb
, const char* key
) {
1750 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
1754 UErrorCode status
= U_ZERO_ERROR
;
1756 const UChar
**names
= NULL
;
1757 char** regions
= NULL
;
1758 int32_t numRegions
= 0;
1762 UResourceBundle
* rbTable
= NULL
;
1763 rbTable
= ures_getByKey(rb
, key
, rbTable
, &status
);
1764 if (U_FAILURE(status
)) {
1768 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * TZDBNAMES_KEYS_SIZE
);
1769 UBool isEmpty
= TRUE
;
1770 if (names
!= NULL
) {
1771 for (int32_t i
= 0; i
< TZDBNAMES_KEYS_SIZE
; i
++) {
1772 status
= U_ZERO_ERROR
;
1773 const UChar
*value
= ures_getStringByKey(rbTable
, TZDBNAMES_KEYS
[i
], &len
, &status
);
1774 if (U_FAILURE(status
) || len
== 0) {
1784 if (names
!= NULL
) {
1790 UResourceBundle
*regionsRes
= ures_getByKey(rbTable
, "parseRegions", NULL
, &status
);
1791 UBool regionError
= FALSE
;
1792 if (U_SUCCESS(status
)) {
1793 numRegions
= ures_getSize(regionsRes
);
1794 if (numRegions
> 0) {
1795 regions
= (char**)uprv_malloc(sizeof(char*) * numRegions
);
1796 if (regions
!= NULL
) {
1797 char **pRegion
= regions
;
1798 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1803 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1804 status
= U_ZERO_ERROR
;
1805 const UChar
*uregion
= ures_getStringByIndex(regionsRes
, i
, &len
, &status
);
1806 if (U_FAILURE(status
)) {
1810 *pRegion
= (char*)uprv_malloc(sizeof(char) * (len
+ 1));
1811 if (*pRegion
== NULL
) {
1815 u_UCharsToChars(uregion
, *pRegion
, len
);
1816 (*pRegion
)[len
] = 0;
1821 ures_close(regionsRes
);
1822 ures_close(rbTable
);
1825 if (names
!= NULL
) {
1828 if (regions
!= NULL
) {
1830 for (int32_t i
= 0; i
< numRegions
; p
++, i
++) {
1838 return new TZDBNames(names
, regions
, numRegions
);
1842 TZDBNames::getName(UTimeZoneNameType type
) const {
1843 if (fNames
== NULL
) {
1846 const UChar
*name
= NULL
;
1848 case UTZNM_SHORT_STANDARD
:
1851 case UTZNM_SHORT_DAYLIGHT
:
1861 TZDBNames::getParseRegions(int32_t& numRegions
) const {
1862 if (fRegions
== NULL
) {
1865 numRegions
= fNumRegions
;
1867 return (const char**)fRegions
;
1872 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1875 typedef struct TZDBNameInfo
{
1877 UTimeZoneNameType type
;
1878 UBool ambiguousType
;
1879 const char** parseRegions
;
1885 class TZDBNameSearchHandler
: public TextTrieMapSearchResultHandler
{
1887 TZDBNameSearchHandler(uint32_t types
, const char* region
);
1888 virtual ~TZDBNameSearchHandler();
1890 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
1891 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
1895 int32_t fMaxMatchLen
;
1896 TimeZoneNames::MatchInfoCollection
* fResults
;
1897 const char* fRegion
;
1900 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types
, const char* region
)
1901 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
), fRegion(region
) {
1904 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1905 if (fResults
!= NULL
) {
1911 TZDBNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
1912 if (U_FAILURE(status
)) {
1916 TZDBNameInfo
*match
= NULL
;
1917 TZDBNameInfo
*defaultRegionMatch
= NULL
;
1919 if (node
->hasValues()) {
1920 int32_t valuesCount
= node
->countValues();
1921 for (int32_t i
= 0; i
< valuesCount
; i
++) {
1922 TZDBNameInfo
*ninfo
= (TZDBNameInfo
*)node
->getValue(i
);
1923 if (ninfo
== NULL
) {
1926 if ((ninfo
->type
& fTypes
) != 0) {
1927 // Some tz database abbreviations are ambiguous. For example,
1928 // CST means either Central Standard Time or China Standard Time.
1929 // Unlike CLDR time zone display names, this implementation
1930 // does not use unique names. And TimeZoneFormat does not expect
1931 // multiple results returned for the same time zone type.
1932 // For this reason, this implementation resolve one among same
1933 // zone type with a same name at this level.
1934 if (ninfo
->parseRegions
== NULL
) {
1935 // parseRegions == null means this is the default metazone
1936 // mapping for the abbreviation.
1937 if (defaultRegionMatch
== NULL
) {
1938 match
= defaultRegionMatch
= ninfo
;
1941 UBool matchRegion
= FALSE
;
1942 // non-default metazone mapping for an abbreviation
1943 // comes with applicable regions. For example, the default
1944 // metazone mapping for "CST" is America_Central,
1945 // but if region is one of CN/MO/TW, "CST" is parsed
1946 // as metazone China (China Standard Time).
1947 for (int32_t i
= 0; i
< ninfo
->nRegions
; i
++) {
1948 const char *region
= ninfo
->parseRegions
[i
];
1949 if (uprv_strcmp(fRegion
, region
) == 0) {
1958 if (match
== NULL
) {
1965 if (match
!= NULL
) {
1966 UTimeZoneNameType ntype
= match
->type
;
1967 // Note: Workaround for duplicated standard/daylight names
1968 // The tz database contains a few zones sharing a
1969 // same name for both standard time and daylight saving
1970 // time. For example, Australia/Sydney observes DST,
1971 // but "EST" is used for both standard and daylight.
1972 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1973 // in the find operation, we cannot tell which one was
1974 // actually matched.
1975 // TimeZoneFormat#parse returns a matched name type (standard
1976 // or daylight) and DateFormat implementation uses the info to
1977 // to adjust actual time. To avoid false type information,
1978 // this implementation replaces the name type with SHORT_GENERIC.
1979 if (match
->ambiguousType
1980 && (ntype
== UTZNM_SHORT_STANDARD
|| ntype
== UTZNM_SHORT_DAYLIGHT
)
1981 && (fTypes
& UTZNM_SHORT_STANDARD
) != 0
1982 && (fTypes
& UTZNM_SHORT_DAYLIGHT
) != 0) {
1983 ntype
= UTZNM_SHORT_GENERIC
;
1986 if (fResults
== NULL
) {
1987 fResults
= new TimeZoneNames::MatchInfoCollection();
1988 if (fResults
== NULL
) {
1989 status
= U_MEMORY_ALLOCATION_ERROR
;
1992 if (U_SUCCESS(status
)) {
1993 U_ASSERT(fResults
!= NULL
);
1994 U_ASSERT(match
->mzID
!= NULL
);
1995 fResults
->addMetaZone(ntype
, matchLength
, UnicodeString(match
->mzID
, -1), status
);
1996 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
1997 fMaxMatchLen
= matchLength
;
2005 TimeZoneNames::MatchInfoCollection
*
2006 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
2007 // give the ownership to the caller
2008 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
2009 maxMatchLen
= fMaxMatchLen
;
2019 * Deleter for TZDBNames
2021 static void U_CALLCONV
2022 deleteTZDBNames(void *obj
) {
2024 delete (TZDBNames
*)obj
;
2028 static void U_CALLCONV
initTZDBNamesMap(UErrorCode
&status
) {
2029 gTZDBNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2030 if (U_FAILURE(status
)) {
2031 gTZDBNamesMap
= NULL
;
2034 // no key deleters for tzdb name maps
2035 uhash_setValueDeleter(gTZDBNamesMap
, deleteTZDBNames
);
2036 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2040 * Deleter for TZDBNameInfo
2042 static void U_CALLCONV
2043 deleteTZDBNameInfo(void *obj
) {
2049 static void U_CALLCONV
prepareFind(UErrorCode
&status
) {
2050 if (U_FAILURE(status
)) {
2053 gTZDBNamesTrie
= new TextTrieMap(TRUE
, deleteTZDBNameInfo
);
2054 if (gTZDBNamesTrie
== NULL
) {
2055 status
= U_MEMORY_ALLOCATION_ERROR
;
2059 const UnicodeString
*mzID
;
2060 StringEnumeration
*mzIDs
= TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2061 if (U_SUCCESS(status
)) {
2062 while ((mzID
= mzIDs
->snext(status
)) && U_SUCCESS(status
)) {
2063 const TZDBNames
*names
= TZDBTimeZoneNames::getMetaZoneNames(*mzID
, status
);
2064 if (U_FAILURE(status
)) {
2067 if (names
== NULL
) {
2070 const UChar
*std
= names
->getName(UTZNM_SHORT_STANDARD
);
2071 const UChar
*dst
= names
->getName(UTZNM_SHORT_DAYLIGHT
);
2072 if (std
== NULL
&& dst
== NULL
) {
2075 int32_t numRegions
= 0;
2076 const char **parseRegions
= names
->getParseRegions(numRegions
);
2078 // The tz database contains a few zones sharing a
2079 // same name for both standard time and daylight saving
2080 // time. For example, Australia/Sydney observes DST,
2081 // but "EST" is used for both standard and daylight.
2082 // we need to store the information for later processing.
2083 UBool ambiguousType
= (std
!= NULL
&& dst
!= NULL
&& u_strcmp(std
, dst
) == 0);
2085 const UChar
*uMzID
= ZoneMeta::findMetaZoneID(*mzID
);
2087 TZDBNameInfo
*stdInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2088 if (stdInf
== NULL
) {
2089 status
= U_MEMORY_ALLOCATION_ERROR
;
2092 stdInf
->mzID
= uMzID
;
2093 stdInf
->type
= UTZNM_SHORT_STANDARD
;
2094 stdInf
->ambiguousType
= ambiguousType
;
2095 stdInf
->parseRegions
= parseRegions
;
2096 stdInf
->nRegions
= numRegions
;
2097 gTZDBNamesTrie
->put(std
, stdInf
, status
);
2099 if (U_SUCCESS(status
) && dst
!= NULL
) {
2100 TZDBNameInfo
*dstInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2101 if (dstInf
== NULL
) {
2102 status
= U_MEMORY_ALLOCATION_ERROR
;
2105 dstInf
->mzID
= uMzID
;
2106 dstInf
->type
= UTZNM_SHORT_DAYLIGHT
;
2107 dstInf
->ambiguousType
= ambiguousType
;
2108 dstInf
->parseRegions
= parseRegions
;
2109 dstInf
->nRegions
= numRegions
;
2110 gTZDBNamesTrie
->put(dst
, dstInf
, status
);
2116 if (U_FAILURE(status
)) {
2117 delete gTZDBNamesTrie
;
2118 gTZDBNamesTrie
= NULL
;
2122 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2127 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale
& locale
)
2129 UBool useWorld
= TRUE
;
2130 const char* region
= fLocale
.getCountry();
2131 int32_t regionLen
= uprv_strlen(region
);
2132 if (regionLen
== 0) {
2133 UErrorCode status
= U_ZERO_ERROR
;
2134 char loc
[ULOC_FULLNAME_CAPACITY
];
2135 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
2136 regionLen
= uloc_getCountry(loc
, fRegion
, sizeof(fRegion
), &status
);
2137 if (U_SUCCESS(status
) && regionLen
< (int32_t)sizeof(fRegion
)) {
2140 } else if (regionLen
< (int32_t)sizeof(fRegion
)) {
2141 uprv_strcpy(fRegion
, region
);
2145 uprv_strcpy(fRegion
, "001");
2149 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2153 TZDBTimeZoneNames::operator==(const TimeZoneNames
& other
) const {
2154 if (this == &other
) {
2157 // No implementation for now
2162 TZDBTimeZoneNames::clone() const {
2163 return new TZDBTimeZoneNames(fLocale
);
2167 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
2168 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2172 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
2173 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
2177 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
2178 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
2182 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
2183 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
2187 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString
& mzID
,
2188 UTimeZoneNameType type
,
2189 UnicodeString
& name
) const {
2191 if (mzID
.isEmpty()) {
2195 UErrorCode status
= U_ZERO_ERROR
;
2196 const TZDBNames
*tzdbNames
= TZDBTimeZoneNames::getMetaZoneNames(mzID
, status
);
2197 if (U_SUCCESS(status
)) {
2198 if (tzdbNames
!= NULL
) {
2199 const UChar
*s
= tzdbNames
->getName(type
);
2201 name
.setTo(TRUE
, s
, -1);
2210 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString
& /* tzID */, UTimeZoneNameType
/* type */, UnicodeString
& name
) const {
2211 // No abbreviations associated a zone directly for now.
2216 TZDBTimeZoneNames::MatchInfoCollection
*
2217 TZDBTimeZoneNames::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
2218 umtx_initOnce(gTZDBNamesTrieInitOnce
, &prepareFind
, status
);
2219 if (U_FAILURE(status
)) {
2223 TZDBNameSearchHandler
handler(types
, fRegion
);
2224 gTZDBNamesTrie
->search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
2225 if (U_FAILURE(status
)) {
2229 return handler
.getMatches(maxLen
);
2233 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
2234 umtx_initOnce(gTZDBNamesMapInitOnce
, &initTZDBNamesMap
, status
);
2235 if (U_FAILURE(status
)) {
2239 TZDBNames
* tzdbNames
= NULL
;
2241 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
2242 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
2243 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
2244 mzIDKey
[mzID
.length()] = 0;
2246 umtx_lock(&gTZDBNamesMapLock
);
2248 void *cacheVal
= uhash_get(gTZDBNamesMap
, mzIDKey
);
2249 if (cacheVal
== NULL
) {
2250 UResourceBundle
*zoneStringsRes
= ures_openDirect(U_ICUDATA_ZONE
, "tzdbNames", &status
);
2251 zoneStringsRes
= ures_getByKey(zoneStringsRes
, gZoneStrings
, zoneStringsRes
, &status
);
2252 if (U_SUCCESS(status
)) {
2253 char key
[ZID_KEY_MAX
+ 1];
2254 mergeTimeZoneKey(mzID
, key
);
2255 tzdbNames
= TZDBNames::createInstance(zoneStringsRes
, key
);
2257 if (tzdbNames
== NULL
) {
2258 cacheVal
= (void *)EMPTY
;
2260 cacheVal
= tzdbNames
;
2262 // Use the persistent ID as the resource key, so we can
2263 // avoid duplications.
2264 // TODO: Is there a more efficient way, like intern() in Java?
2265 void* newKey
= (void*) ZoneMeta::findMetaZoneID(mzID
);
2266 if (newKey
!= NULL
) {
2267 uhash_put(gTZDBNamesMap
, newKey
, cacheVal
, &status
);
2268 if (U_FAILURE(status
)) {
2269 if (tzdbNames
!= NULL
) {
2275 // Should never happen with a valid input
2276 if (tzdbNames
!= NULL
) {
2277 // It's not possible that we get a valid tzdbNames with unknown ID.
2278 // But just in case..
2284 ures_close(zoneStringsRes
);
2285 } else if (cacheVal
!= EMPTY
) {
2286 tzdbNames
= (TZDBNames
*)cacheVal
;
2289 umtx_unlock(&gTZDBNamesMapLock
);
2297 #endif /* #if !UCONFIG_NO_FORMATTING */