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"
22 #include "tznames_impl.h"
37 #define ZID_KEY_MAX 128
38 #define MZ_PREFIX_LEN 5
40 static const char gZoneStrings
[] = "zoneStrings";
41 static const char gMZPrefix
[] = "meta:";
43 static const char EMPTY
[] = "<empty>"; // place holder for empty ZNames
44 static const char DUMMY_LOADER
[] = "<dummy>"; // place holder for dummy ZNamesLoader
45 static const UChar NO_NAME
[] = { 0 }; // for empty no-fallback time zone names
47 // stuff for TZDBTimeZoneNames
48 static const char* TZDBNAMES_KEYS
[] = {"ss", "sd"};
49 static const int32_t TZDBNAMES_KEYS_SIZE
= UPRV_LENGTHOF(TZDBNAMES_KEYS
);
51 static UMutex gTZDBNamesMapLock
= U_MUTEX_INITIALIZER
;
52 static UMutex gDataMutex
= U_MUTEX_INITIALIZER
;
54 static UHashtable
* gTZDBNamesMap
= NULL
;
55 static icu::UInitOnce gTZDBNamesMapInitOnce
= U_INITONCE_INITIALIZER
;
57 static TextTrieMap
* gTZDBNamesTrie
= NULL
;
58 static icu::UInitOnce gTZDBNamesTrieInitOnce
= U_INITONCE_INITIALIZER
;
60 // The order in which strings are stored may be different than the order in the public enum.
61 enum UTimeZoneNameTypeIndex
{
62 UTZNM_INDEX_UNKNOWN
= -1,
63 UTZNM_INDEX_EXEMPLAR_LOCATION
,
64 UTZNM_INDEX_LONG_GENERIC
,
65 UTZNM_INDEX_LONG_STANDARD
,
66 UTZNM_INDEX_LONG_DAYLIGHT
,
67 UTZNM_INDEX_SHORT_GENERIC
,
68 UTZNM_INDEX_SHORT_STANDARD
,
69 UTZNM_INDEX_SHORT_DAYLIGHT
,
72 static const UChar
* EMPTY_NAMES
[UTZNM_INDEX_COUNT
] = {0,0,0,0,0,0,0};
75 static UBool U_CALLCONV
tzdbTimeZoneNames_cleanup(void) {
76 if (gTZDBNamesMap
!= NULL
) {
77 uhash_close(gTZDBNamesMap
);
80 gTZDBNamesMapInitOnce
.reset();
82 if (gTZDBNamesTrie
!= NULL
) {
83 delete gTZDBNamesTrie
;
84 gTZDBNamesTrie
= NULL
;
86 gTZDBNamesTrieInitOnce
.reset();
93 * ZNameInfo stores zone name information in the trie
96 UTimeZoneNameType type
;
102 * ZMatchInfo stores zone name match information used by find method
105 const ZNameInfo
* znameInfo
;
110 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
);
112 #define DEFAULT_CHARACTERNODE_CAPACITY 1
114 // ---------------------------------------------------
115 // CharacterNode class implementation
116 // ---------------------------------------------------
117 void CharacterNode::clear() {
118 uprv_memset(this, 0, sizeof(*this));
121 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
122 if (fValues
== NULL
) {
124 } else if (!fHasValuesVector
) {
126 valueDeleter(fValues
);
129 delete (UVector
*)fValues
;
134 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
135 if (U_FAILURE(status
)) {
141 if (fValues
== NULL
) {
144 // At least one value already.
145 if (!fHasValuesVector
) {
146 // There is only one value so far, and not in a vector yet.
147 // Create a vector and add the old value.
148 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
149 if (U_FAILURE(status
)) {
155 values
->addElement(fValues
, status
);
157 fHasValuesVector
= TRUE
;
159 // Add the new value.
160 ((UVector
*)fValues
)->addElement(value
, status
);
164 // ---------------------------------------------------
165 // TextTrieMapSearchResultHandler class implementation
166 // ---------------------------------------------------
167 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
170 // ---------------------------------------------------
171 // TextTrieMap class implementation
172 // ---------------------------------------------------
173 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
174 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
175 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
178 TextTrieMap::~TextTrieMap() {
180 for (index
= 0; index
< fNodesCount
; ++index
) {
181 fNodes
[index
].deleteValues(fValueDeleter
);
184 if (fLazyContents
!= NULL
) {
185 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
187 fValueDeleter(fLazyContents
->elementAt(i
+1));
190 delete fLazyContents
;
194 int32_t TextTrieMap::isEmpty() const {
195 // Use a separate field for fIsEmpty because it will remain unchanged once the
196 // Trie is built, while fNodes and fLazyContents change with the lazy init
197 // of the nodes structure. Trying to test the changing fields has
198 // thread safety complications.
203 // We defer actually building the TextTrieMap node structure until the first time a
204 // search is performed. put() simply saves the parameters in case we do
205 // eventually need to build it.
208 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
209 const UChar
*s
= sp
.get(key
, status
);
210 put(s
, value
, status
);
213 // This method is designed for a persistent key, such as string key stored in
216 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
218 if (fLazyContents
== NULL
) {
219 fLazyContents
= new UVector(status
);
220 if (fLazyContents
== NULL
) {
221 status
= U_MEMORY_ALLOCATION_ERROR
;
224 if (U_FAILURE(status
)) {
226 fValueDeleter((void*) key
);
230 U_ASSERT(fLazyContents
!= NULL
);
232 UChar
*s
= const_cast<UChar
*>(key
);
233 fLazyContents
->addElement(s
, status
);
234 if (U_FAILURE(status
)) {
236 fValueDeleter((void*) key
);
241 fLazyContents
->addElement(value
, status
);
245 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
246 if (fNodes
== NULL
) {
247 fNodesCapacity
= 512;
248 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
249 if (fNodes
== NULL
) {
250 status
= U_MEMORY_ALLOCATION_ERROR
;
253 fNodes
[0].clear(); // Init root node.
257 UnicodeString foldedKey
;
258 const UChar
*keyBuffer
;
261 // Ok to use fastCopyFrom() because we discard the copy when we return.
262 foldedKey
.fastCopyFrom(key
).foldCase();
263 keyBuffer
= foldedKey
.getBuffer();
264 keyLength
= foldedKey
.length();
266 keyBuffer
= key
.getBuffer();
267 keyLength
= key
.length();
270 CharacterNode
*node
= fNodes
;
272 for (index
= 0; index
< keyLength
; ++index
) {
273 node
= addChildNode(node
, keyBuffer
[index
], status
);
275 node
->addValue(value
, fValueDeleter
, status
);
279 TextTrieMap::growNodes() {
280 if (fNodesCapacity
== 0xffff) {
281 return FALSE
; // We use 16-bit node indexes.
283 int32_t newCapacity
= fNodesCapacity
+ 1000;
284 if (newCapacity
> 0xffff) {
285 newCapacity
= 0xffff;
287 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
288 if (newNodes
== NULL
) {
291 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
294 fNodesCapacity
= newCapacity
;
299 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
300 if (U_FAILURE(status
)) {
303 // Linear search of the sorted list of children.
304 uint16_t prevIndex
= 0;
305 uint16_t nodeIndex
= parent
->fFirstChild
;
306 while (nodeIndex
> 0) {
307 CharacterNode
*current
= fNodes
+ nodeIndex
;
308 UChar childCharacter
= current
->fCharacter
;
309 if (childCharacter
== c
) {
311 } else if (childCharacter
> c
) {
314 prevIndex
= nodeIndex
;
315 nodeIndex
= current
->fNextSibling
;
318 // Ensure capacity. Grow fNodes[] if needed.
319 if (fNodesCount
== fNodesCapacity
) {
320 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
322 status
= U_MEMORY_ALLOCATION_ERROR
;
325 parent
= fNodes
+ parentIndex
;
328 // Insert a new child node with c in sorted order.
329 CharacterNode
*node
= fNodes
+ fNodesCount
;
331 node
->fCharacter
= c
;
332 node
->fNextSibling
= nodeIndex
;
333 if (prevIndex
== 0) {
334 parent
->fFirstChild
= (uint16_t)fNodesCount
;
336 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
343 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
344 // Linear search of the sorted list of children.
345 uint16_t nodeIndex
= parent
->fFirstChild
;
346 while (nodeIndex
> 0) {
347 CharacterNode
*current
= fNodes
+ nodeIndex
;
348 UChar childCharacter
= current
->fCharacter
;
349 if (childCharacter
== c
) {
351 } else if (childCharacter
> c
) {
354 nodeIndex
= current
->fNextSibling
;
359 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
360 static UMutex TextTrieMutex
= U_MUTEX_INITIALIZER
;
362 // buildTrie() - The Trie node structure is needed. Create it from the data that was
363 // saved at the time the ZoneStringFormatter was created. The Trie is only
364 // needed for parsing operations, which are less common than formatting,
365 // and the Trie is big, which is why its creation is deferred until first use.
366 void TextTrieMap::buildTrie(UErrorCode
&status
) {
367 if (fLazyContents
!= NULL
) {
368 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
369 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
370 void *val
= fLazyContents
->elementAt(i
+1);
371 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
372 putImpl(keyString
, val
, status
);
374 delete fLazyContents
;
375 fLazyContents
= NULL
;
380 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
381 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
383 // TODO: if locking the mutex for each check proves to be a performance problem,
384 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
385 // the ICU atomic safe functions for assigning and testing.
386 // Don't test the pointer fLazyContents.
387 // Don't do unless it's really required.
388 Mutex
lock(&TextTrieMutex
);
389 if (fLazyContents
!= NULL
) {
390 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
391 nonConstThis
->buildTrie(status
);
394 if (fNodes
== NULL
) {
397 search(fNodes
, text
, start
, start
, handler
, status
);
401 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
402 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
403 if (U_FAILURE(status
)) {
406 if (node
->hasValues()) {
407 if (!handler
->handleMatch(index
- start
, node
, status
)) {
410 if (U_FAILURE(status
)) {
414 UChar32 c
= text
.char32At(index
);
416 // size of character may grow after fold operation
417 UnicodeString
tmp(c
);
420 while (tmpidx
< tmp
.length()) {
421 c
= tmp
.char32At(tmpidx
);
422 node
= getChildNode(node
, c
);
426 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
429 node
= getChildNode(node
, c
);
432 search(node
, text
, start
, index
+1, handler
, status
);
436 // ---------------------------------------------------
437 // ZNStringPool class implementation
438 // ---------------------------------------------------
439 static const int32_t POOL_CHUNK_SIZE
= 2000;
440 struct ZNStringPoolChunk
: public UMemory
{
441 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
442 int32_t fLimit
; // Index to start of unused area at end of fStrings
443 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
447 ZNStringPoolChunk::ZNStringPoolChunk() {
452 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
455 if (U_FAILURE(status
)) {
458 fChunks
= new ZNStringPoolChunk
;
459 if (fChunks
== NULL
) {
460 status
= U_MEMORY_ALLOCATION_ERROR
;
464 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
465 uhash_compareUChars
/* keyComp */,
466 uhash_compareUChars
/* valueComp */,
468 if (U_FAILURE(status
)) {
473 ZNStringPool::~ZNStringPool() {
479 while (fChunks
!= NULL
) {
480 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
486 static const UChar EmptyString
= 0;
488 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
489 const UChar
*pooledString
;
490 if (U_FAILURE(status
)) {
494 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
495 if (pooledString
!= NULL
) {
499 int32_t length
= u_strlen(s
);
500 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
501 if (remainingLength
<= length
) {
502 U_ASSERT(length
< POOL_CHUNK_SIZE
);
503 if (length
>= POOL_CHUNK_SIZE
) {
504 status
= U_INTERNAL_PROGRAM_ERROR
;
507 ZNStringPoolChunk
*oldChunk
= fChunks
;
508 fChunks
= new ZNStringPoolChunk
;
509 if (fChunks
== NULL
) {
510 status
= U_MEMORY_ALLOCATION_ERROR
;
513 fChunks
->fNext
= oldChunk
;
516 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
517 u_strcpy(destString
, s
);
518 fChunks
->fLimit
+= (length
+ 1);
519 uhash_put(fHash
, destString
, destString
, &status
);
525 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
526 // into the pool's storage. Used for strings from resource bundles,
527 // which will perisist for the life of the zone string formatter, and
528 // therefore can be used directly without copying.
529 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
530 const UChar
*pooledString
;
531 if (U_FAILURE(status
)) {
535 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
536 if (pooledString
== NULL
) {
537 UChar
*ncs
= const_cast<UChar
*>(s
);
538 uhash_put(fHash
, ncs
, ncs
, &status
);
545 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
546 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
547 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
551 * freeze(). Close the hash table that maps to the pooled strings.
552 * After freezing, the pool can not be searched or added to,
553 * but all existing references to pooled strings remain valid.
555 * The main purpose is to recover the storage used for the hash.
557 void ZNStringPool::freeze() {
564 * This class stores name data for a meta zone or time zone.
566 class ZNames
: public UMemory
{
568 friend class TimeZoneNamesImpl
;
570 static UTimeZoneNameTypeIndex
getTZNameTypeIndex(UTimeZoneNameType type
) {
572 case UTZNM_EXEMPLAR_LOCATION
: return UTZNM_INDEX_EXEMPLAR_LOCATION
;
573 case UTZNM_LONG_GENERIC
: return UTZNM_INDEX_LONG_GENERIC
;
574 case UTZNM_LONG_STANDARD
: return UTZNM_INDEX_LONG_STANDARD
;
575 case UTZNM_LONG_DAYLIGHT
: return UTZNM_INDEX_LONG_DAYLIGHT
;
576 case UTZNM_SHORT_GENERIC
: return UTZNM_INDEX_SHORT_GENERIC
;
577 case UTZNM_SHORT_STANDARD
: return UTZNM_INDEX_SHORT_STANDARD
;
578 case UTZNM_SHORT_DAYLIGHT
: return UTZNM_INDEX_SHORT_DAYLIGHT
;
579 default: return UTZNM_INDEX_UNKNOWN
;
582 static UTimeZoneNameType
getTZNameType(UTimeZoneNameTypeIndex index
) {
584 case UTZNM_INDEX_EXEMPLAR_LOCATION
: return UTZNM_EXEMPLAR_LOCATION
;
585 case UTZNM_INDEX_LONG_GENERIC
: return UTZNM_LONG_GENERIC
;
586 case UTZNM_INDEX_LONG_STANDARD
: return UTZNM_LONG_STANDARD
;
587 case UTZNM_INDEX_LONG_DAYLIGHT
: return UTZNM_LONG_DAYLIGHT
;
588 case UTZNM_INDEX_SHORT_GENERIC
: return UTZNM_SHORT_GENERIC
;
589 case UTZNM_INDEX_SHORT_STANDARD
: return UTZNM_SHORT_STANDARD
;
590 case UTZNM_INDEX_SHORT_DAYLIGHT
: return UTZNM_SHORT_DAYLIGHT
;
591 default: return UTZNM_UNKNOWN
;
595 const UChar
* fNames
[UTZNM_INDEX_COUNT
];
596 UBool fDidAddIntoTrie
;
598 // Whether we own the location string, if computed rather than loaded from a bundle.
599 // A meta zone names instance never has an exemplar location string.
600 UBool fOwnsLocationName
;
602 ZNames(const UChar
* names
[], const UChar
* locationName
)
603 : fDidAddIntoTrie(FALSE
) {
604 uprv_memcpy(fNames
, names
, sizeof(fNames
));
605 if (locationName
!= NULL
) {
606 fOwnsLocationName
= TRUE
;
607 fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
] = locationName
;
609 fOwnsLocationName
= FALSE
;
615 if (fOwnsLocationName
) {
616 const UChar
* locationName
= fNames
[UTZNM_INDEX_EXEMPLAR_LOCATION
];
617 U_ASSERT(locationName
!= NULL
);
618 uprv_free((void*) locationName
);
623 static void* createMetaZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
624 const UnicodeString
& mzID
, UErrorCode
& status
) {
625 if (U_FAILURE(status
)) { return NULL
; }
626 U_ASSERT(names
!= NULL
);
628 // Use the persistent ID as the resource key, so we can
629 // avoid duplications.
630 // TODO: Is there a more efficient way, like intern() in Java?
631 void* key
= (void*) ZoneMeta::findMetaZoneID(mzID
);
633 if (uprv_memcmp(names
, EMPTY_NAMES
, sizeof(EMPTY_NAMES
)) == 0) {
634 value
= (void*) EMPTY
;
636 value
= (void*) (new ZNames(names
, NULL
));
638 status
= U_MEMORY_ALLOCATION_ERROR
;
642 uhash_put(cache
, key
, value
, &status
);
646 static void* createTimeZoneAndPutInCache(UHashtable
* cache
, const UChar
* names
[],
647 const UnicodeString
& tzID
, UErrorCode
& status
) {
648 if (U_FAILURE(status
)) { return NULL
; }
649 U_ASSERT(names
!= NULL
);
651 // If necessary, compute the location name from the time zone name.
652 UChar
* locationName
= NULL
;
653 if (names
[UTZNM_INDEX_EXEMPLAR_LOCATION
] == NULL
) {
654 UnicodeString locationNameUniStr
;
655 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, locationNameUniStr
);
657 // Copy the computed location name to the heap
658 if (locationNameUniStr
.length() > 0) {
659 const UChar
* buff
= locationNameUniStr
.getTerminatedBuffer();
660 int32_t len
= sizeof(UChar
) * (locationNameUniStr
.length() + 1);
661 locationName
= (UChar
*) uprv_malloc(len
);
662 if (locationName
== NULL
) {
663 status
= U_MEMORY_ALLOCATION_ERROR
;
666 uprv_memcpy(locationName
, buff
, len
);
670 // Use the persistent ID as the resource key, so we can
671 // avoid duplications.
672 // TODO: Is there a more efficient way, like intern() in Java?
673 void* key
= (void*) ZoneMeta::findTimeZoneID(tzID
);
674 void* value
= (void*) (new ZNames(names
, locationName
));
676 status
= U_MEMORY_ALLOCATION_ERROR
;
679 uhash_put(cache
, key
, value
, &status
);
683 const UChar
* getName(UTimeZoneNameType type
) const {
684 UTimeZoneNameTypeIndex index
= getTZNameTypeIndex(type
);
685 return index
>= 0 ? fNames
[index
] : NULL
;
688 void addAsMetaZoneIntoTrie(const UChar
* mzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
689 addNamesIntoTrie(mzID
, NULL
, trie
, status
);
691 void addAsTimeZoneIntoTrie(const UChar
* tzID
, TextTrieMap
& trie
, UErrorCode
& status
) {
692 addNamesIntoTrie(NULL
, tzID
, trie
, status
);
695 void addNamesIntoTrie(const UChar
* mzID
, const UChar
* tzID
, TextTrieMap
& trie
,
696 UErrorCode
& status
) {
697 if (U_FAILURE(status
)) { return; }
698 if (fDidAddIntoTrie
) { return; }
699 fDidAddIntoTrie
= TRUE
;
701 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; i
++) {
702 const UChar
* name
= fNames
[i
];
704 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
705 if (nameinfo
== NULL
) {
706 status
= U_MEMORY_ALLOCATION_ERROR
;
709 nameinfo
->mzID
= mzID
;
710 nameinfo
->tzID
= tzID
;
711 nameinfo
->type
= getTZNameType((UTimeZoneNameTypeIndex
)i
);
712 trie
.put(name
, nameinfo
, status
); // trie.put() takes ownership of the key
713 if (U_FAILURE(status
)) {
724 struct ZNames::ZNamesLoader
: public ResourceSink
{
725 const UChar
*names
[UTZNM_INDEX_COUNT
];
730 virtual ~ZNamesLoader();
732 /** Reset for loading another set of names. */
734 uprv_memcpy(names
, EMPTY_NAMES
, sizeof(names
));
737 void loadMetaZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& mzID
, UErrorCode
& errorCode
) {
738 if (U_FAILURE(errorCode
)) { return; }
740 char key
[ZID_KEY_MAX
+ 1];
741 mergeTimeZoneKey(mzID
, key
);
743 loadNames(zoneStrings
, key
, errorCode
);
746 void loadTimeZone(const UResourceBundle
* zoneStrings
, const UnicodeString
& tzID
, UErrorCode
& errorCode
) {
747 // Replace "/" with ":".
748 UnicodeString
uKey(tzID
);
749 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
750 if (uKey
.charAt(i
) == (UChar
)0x2F) {
751 uKey
.setCharAt(i
, (UChar
)0x3A);
755 char key
[ZID_KEY_MAX
+ 1];
756 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
758 loadNames(zoneStrings
, key
, errorCode
);
761 void loadNames(const UResourceBundle
* zoneStrings
, const char* key
, UErrorCode
& errorCode
) {
762 U_ASSERT(zoneStrings
!= NULL
);
763 U_ASSERT(key
!= NULL
);
764 U_ASSERT(key
[0] != '\0');
766 UErrorCode localStatus
= U_ZERO_ERROR
;
768 ures_getAllItemsWithFallback(zoneStrings
, key
, *this, localStatus
);
770 // Ignore errors, but propogate possible warnings.
771 if (U_SUCCESS(localStatus
)) {
772 errorCode
= localStatus
;
776 void setNameIfEmpty(const char* key
, const ResourceValue
* value
, UErrorCode
& errorCode
) {
777 UTimeZoneNameTypeIndex type
= nameTypeFromKey(key
);
778 if (type
== UTZNM_INDEX_UNKNOWN
) { return; }
779 if (names
[type
] == NULL
) {
781 // 'NO_NAME' indicates internally that this field should remain empty. It will be
782 // replaced by 'NULL' in getNames()
783 names
[type
] = (value
== NULL
) ? NO_NAME
: value
->getString(length
, errorCode
);
787 virtual void put(const char* key
, ResourceValue
& value
, UBool
/*noFallback*/,
788 UErrorCode
&errorCode
) {
789 ResourceTable namesTable
= value
.getTable(errorCode
);
790 if (U_FAILURE(errorCode
)) { return; }
791 for (int32_t i
= 0; namesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
792 if (value
.isNoInheritanceMarker()) {
793 setNameIfEmpty(key
, NULL
, errorCode
);
795 setNameIfEmpty(key
, &value
, errorCode
);
800 static UTimeZoneNameTypeIndex
nameTypeFromKey(const char *key
) {
802 if ((c0
= key
[0]) == 0 || (c1
= key
[1]) == 0 || key
[2] != 0) {
803 return UTZNM_INDEX_UNKNOWN
;
806 return c1
== 'g' ? UTZNM_INDEX_LONG_GENERIC
:
807 c1
== 's' ? UTZNM_INDEX_LONG_STANDARD
:
808 c1
== 'd' ? UTZNM_INDEX_LONG_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
809 } else if (c0
== 's') {
810 return c1
== 'g' ? UTZNM_INDEX_SHORT_GENERIC
:
811 c1
== 's' ? UTZNM_INDEX_SHORT_STANDARD
:
812 c1
== 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT
: UTZNM_INDEX_UNKNOWN
;
813 } else if (c0
== 'e' && c1
== 'c') {
814 return UTZNM_INDEX_EXEMPLAR_LOCATION
;
816 return UTZNM_INDEX_UNKNOWN
;
820 * Returns an array of names. It is the caller's responsibility to copy the data into a
821 * permanent location, as the returned array is owned by the loader instance and may be
822 * cleared or leave scope.
824 * This is different than Java, where the array will no longer be modified and null
827 const UChar
** getNames() {
828 // Remove 'NO_NAME' references in the array and replace with 'NULL'
829 for (int32_t i
= 0; i
< UTZNM_INDEX_COUNT
; ++i
) {
830 if (names
[i
] == NO_NAME
) {
838 ZNames::ZNamesLoader::~ZNamesLoader() {}
841 // ---------------------------------------------------
842 // The meta zone ID enumeration class
843 // ---------------------------------------------------
844 class MetaZoneIDsEnumeration
: public StringEnumeration
{
846 MetaZoneIDsEnumeration();
847 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
848 MetaZoneIDsEnumeration(UVector
* mzIDs
);
849 virtual ~MetaZoneIDsEnumeration();
850 static UClassID U_EXPORT2
getStaticClassID(void);
851 virtual UClassID
getDynamicClassID(void) const;
852 virtual const UnicodeString
* snext(UErrorCode
& status
);
853 virtual void reset(UErrorCode
& status
);
854 virtual int32_t count(UErrorCode
& status
) const;
858 const UVector
* fMetaZoneIDs
;
859 UVector
*fLocalVector
;
862 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
864 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
865 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
868 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
869 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
870 fLen
= fMetaZoneIDs
->size();
873 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
874 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
876 fLen
= fMetaZoneIDs
->size();
881 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
882 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
883 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
890 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
895 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
899 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
906 // ---------------------------------------------------
907 // ZNameSearchHandler
908 // ---------------------------------------------------
909 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
911 ZNameSearchHandler(uint32_t types
);
912 virtual ~ZNameSearchHandler();
914 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
915 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
919 int32_t fMaxMatchLen
;
920 TimeZoneNames::MatchInfoCollection
* fResults
;
923 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
924 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
927 ZNameSearchHandler::~ZNameSearchHandler() {
928 if (fResults
!= NULL
) {
934 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
935 if (U_FAILURE(status
)) {
938 if (node
->hasValues()) {
939 int32_t valuesCount
= node
->countValues();
940 for (int32_t i
= 0; i
< valuesCount
; i
++) {
941 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
942 if (nameinfo
== NULL
) {
945 if ((nameinfo
->type
& fTypes
) != 0) {
946 // matches a requested type
947 if (fResults
== NULL
) {
948 fResults
= new TimeZoneNames::MatchInfoCollection();
949 if (fResults
== NULL
) {
950 status
= U_MEMORY_ALLOCATION_ERROR
;
953 if (U_SUCCESS(status
)) {
954 U_ASSERT(fResults
!= NULL
);
955 if (nameinfo
->tzID
) {
956 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
958 U_ASSERT(nameinfo
->mzID
);
959 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
961 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
962 fMaxMatchLen
= matchLength
;
971 TimeZoneNames::MatchInfoCollection
*
972 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
973 // give the ownership to the caller
974 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
975 maxMatchLen
= fMaxMatchLen
;
983 // ---------------------------------------------------
986 // TimeZoneNames implementation class. This is the main
987 // part of this module.
988 // ---------------------------------------------------
994 static void U_CALLCONV
995 deleteZNames(void *obj
) {
997 delete (ZNames
*) obj
;
1002 * Deleter for ZNameInfo
1004 static void U_CALLCONV
1005 deleteZNameInfo(void *obj
) {
1011 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
1016 fNamesTrieFullyLoaded(FALSE
),
1017 fNamesFullyLoaded(FALSE
),
1018 fNamesTrie(TRUE
, deleteZNameInfo
) {
1019 initialize(locale
, status
);
1023 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
1024 if (U_FAILURE(status
)) {
1028 // Load zoneStrings bundle
1029 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
1030 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
1031 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
1032 if (U_FAILURE(tmpsts
)) {
1038 // Initialize hashtables holding time zone/meta zone names
1039 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1040 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1041 if (U_FAILURE(status
)) {
1046 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
1047 uhash_setValueDeleter(fTZNamesMap
, deleteZNames
);
1048 // no key deleters for name maps
1050 // preload zone strings for the default zone
1051 TimeZone
*tz
= TimeZone::createDefault();
1052 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
1054 loadStrings(UnicodeString(tzID
), status
);
1062 * This method updates the cache and must be called with a lock,
1063 * except initializer.
1066 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
, UErrorCode
& status
) {
1067 loadTimeZoneNames(tzCanonicalID
, status
);
1068 LocalPointer
<StringEnumeration
> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID
, status
));
1069 if (U_FAILURE(status
)) { return; }
1070 U_ASSERT(!mzIDs
.isNull());
1072 const UnicodeString
*mzID
;
1073 while ((mzID
= mzIDs
->snext(status
)) && U_SUCCESS(status
)) {
1074 loadMetaZoneNames(*mzID
, status
);
1078 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1083 TimeZoneNamesImpl::cleanup() {
1084 if (fZoneStrings
!= NULL
) {
1085 ures_close(fZoneStrings
);
1086 fZoneStrings
= NULL
;
1088 if (fMZNamesMap
!= NULL
) {
1089 uhash_close(fMZNamesMap
);
1092 if (fTZNamesMap
!= NULL
) {
1093 uhash_close(fTZNamesMap
);
1099 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
1100 if (this == &other
) {
1103 // No implementation for now
1108 TimeZoneNamesImpl::clone() const {
1109 UErrorCode status
= U_ZERO_ERROR
;
1110 return new TimeZoneNamesImpl(fLocale
, status
);
1114 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
1115 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1118 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1120 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode
& status
) {
1121 if (U_FAILURE(status
)) {
1124 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
1125 if (mzIDs
== NULL
) {
1126 return new MetaZoneIDsEnumeration();
1128 return new MetaZoneIDsEnumeration(*mzIDs
);
1132 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1133 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
1136 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1138 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) {
1139 if (U_FAILURE(status
)) {
1142 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1143 if (mappings
== NULL
) {
1144 return new MetaZoneIDsEnumeration();
1147 MetaZoneIDsEnumeration
*senum
= NULL
;
1148 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1149 if (mzIDs
== NULL
) {
1150 status
= U_MEMORY_ALLOCATION_ERROR
;
1152 if (U_SUCCESS(status
)) {
1153 U_ASSERT(mzIDs
!= NULL
);
1154 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1156 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1157 const UChar
*mzID
= map
->mzid
;
1158 if (!mzIDs
->contains((void *)mzID
)) {
1159 mzIDs
->addElement((void *)mzID
, status
);
1162 if (U_SUCCESS(status
)) {
1163 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1172 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1173 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
1176 // static implementation of getMetaZoneID
1178 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) {
1179 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1184 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1185 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
1188 // static implementaion of getReferenceZoneID
1190 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) {
1191 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1196 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1197 UTimeZoneNameType type
,
1198 UnicodeString
& name
) const {
1199 name
.setToBogus(); // cleanup result.
1200 if (mzID
.isEmpty()) {
1204 ZNames
*znames
= NULL
;
1205 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1208 Mutex
lock(&gDataMutex
);
1209 UErrorCode status
= U_ZERO_ERROR
;
1210 znames
= nonConstThis
->loadMetaZoneNames(mzID
, status
);
1211 if (U_FAILURE(status
)) { return name
; }
1214 if (znames
!= NULL
) {
1215 const UChar
* s
= znames
->getName(type
);
1217 name
.setTo(TRUE
, s
, -1);
1224 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1225 name
.setToBogus(); // cleanup result.
1226 if (tzID
.isEmpty()) {
1230 ZNames
*tznames
= NULL
;
1231 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1234 Mutex
lock(&gDataMutex
);
1235 UErrorCode status
= U_ZERO_ERROR
;
1236 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1237 if (U_FAILURE(status
)) { return name
; }
1240 if (tznames
!= NULL
) {
1241 const UChar
*s
= tznames
->getName(type
);
1243 name
.setTo(TRUE
, s
, -1);
1250 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1251 name
.setToBogus(); // cleanup result.
1252 const UChar
* locName
= NULL
;
1253 ZNames
*tznames
= NULL
;
1254 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1257 Mutex
lock(&gDataMutex
);
1258 UErrorCode status
= U_ZERO_ERROR
;
1259 tznames
= nonConstThis
->loadTimeZoneNames(tzID
, status
);
1260 if (U_FAILURE(status
)) { return name
; }
1263 if (tznames
!= NULL
) {
1264 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1266 if (locName
!= NULL
) {
1267 name
.setTo(TRUE
, locName
, -1);
1274 // Merge the MZ_PREFIX and mzId
1275 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1276 if (mzID
.isEmpty()) {
1281 char mzIdChar
[ZID_KEY_MAX
+ 1];
1283 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1284 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1285 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1286 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1287 result
[keyLen
+ prefixLen
] = '\0';
1291 * This method updates the cache and must be called with a lock
1294 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
1295 if (U_FAILURE(status
)) { return NULL
; }
1296 U_ASSERT(mzID
.length() <= ZID_KEY_MAX
- MZ_PREFIX_LEN
);
1298 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1299 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1300 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1301 mzIDKey
[mzID
.length()] = 0;
1303 void* mznames
= uhash_get(fMZNamesMap
, mzIDKey
);
1304 if (mznames
== NULL
) {
1305 ZNames::ZNamesLoader loader
;
1306 loader
.loadMetaZone(fZoneStrings
, mzID
, status
);
1307 mznames
= ZNames::createMetaZoneAndPutInCache(fMZNamesMap
, loader
.getNames(), mzID
, status
);
1308 if (U_FAILURE(status
)) { return NULL
; }
1311 if (mznames
!= EMPTY
) {
1312 return (ZNames
*)mznames
;
1319 * This method updates the cache and must be called with a lock
1322 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
, UErrorCode
& status
) {
1323 if (U_FAILURE(status
)) { return NULL
; }
1324 U_ASSERT(tzID
.length() <= ZID_KEY_MAX
);
1326 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1327 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1328 U_ASSERT(U_SUCCESS(status
)); // already checked length above
1329 tzIDKey
[tzIDKeyLen
] = 0;
1331 void *tznames
= uhash_get(fTZNamesMap
, tzIDKey
);
1332 if (tznames
== NULL
) {
1333 ZNames::ZNamesLoader loader
;
1334 loader
.loadTimeZone(fZoneStrings
, tzID
, status
);
1335 tznames
= ZNames::createTimeZoneAndPutInCache(fTZNamesMap
, loader
.getNames(), tzID
, status
);
1336 if (U_FAILURE(status
)) { return NULL
; }
1339 // tznames is never EMPTY
1340 return (ZNames
*)tznames
;
1343 TimeZoneNames::MatchInfoCollection
*
1344 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1345 ZNameSearchHandler
handler(types
);
1346 TimeZoneNames::MatchInfoCollection
* matches
;
1347 TimeZoneNamesImpl
* nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1349 // Synchronize so that data is not loaded multiple times.
1350 // TODO: Consider more fine-grained synchronization.
1352 Mutex
lock(&gDataMutex
);
1354 // First try of lookup.
1355 matches
= doFind(handler
, text
, start
, status
);
1356 if (U_FAILURE(status
)) { return NULL
; }
1357 if (matches
!= NULL
) {
1361 // All names are not yet loaded into the trie.
1362 // We may have loaded names for formatting several time zones,
1363 // and might be parsing one of those.
1364 // Populate the parsing trie from all of the already-loaded names.
1365 nonConstThis
->addAllNamesIntoTrie(status
);
1367 // Second try of lookup.
1368 matches
= doFind(handler
, text
, start
, status
);
1369 if (U_FAILURE(status
)) { return NULL
; }
1370 if (matches
!= NULL
) {
1374 // There are still some names we haven't loaded into the trie yet.
1375 // Load everything now.
1376 nonConstThis
->internalLoadAllDisplayNames(status
);
1377 nonConstThis
->addAllNamesIntoTrie(status
);
1378 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1379 if (U_FAILURE(status
)) { return NULL
; }
1381 // Third try: we must return this one.
1382 return doFind(handler
, text
, start
, status
);
1386 TimeZoneNames::MatchInfoCollection
*
1387 TimeZoneNamesImpl::doFind(ZNameSearchHandler
& handler
,
1388 const UnicodeString
& text
, int32_t start
, UErrorCode
& status
) const {
1390 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1391 if (U_FAILURE(status
)) { return NULL
; }
1394 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1395 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1396 // perfect match, or no more names available
1403 // Caller must synchronize.
1404 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode
& status
) {
1405 if (U_FAILURE(status
)) return;
1407 const UHashElement
* element
;
1410 while ((element
= uhash_nextElement(fMZNamesMap
, &pos
)) != NULL
) {
1411 if (element
->value
.pointer
== EMPTY
) { continue; }
1412 UChar
* mzID
= (UChar
*) element
->key
.pointer
;
1413 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1414 znames
->addAsMetaZoneIntoTrie(mzID
, fNamesTrie
, status
);
1415 if (U_FAILURE(status
)) { return; }
1419 while ((element
= uhash_nextElement(fTZNamesMap
, &pos
)) != NULL
) {
1420 if (element
->value
.pointer
== EMPTY
) { continue; }
1421 UChar
* tzID
= (UChar
*) element
->key
.pointer
;
1422 ZNames
* znames
= (ZNames
*) element
->value
.pointer
;
1423 znames
->addAsTimeZoneIntoTrie(tzID
, fNamesTrie
, status
);
1424 if (U_FAILURE(status
)) { return; }
1429 static void U_CALLCONV
1430 deleteZNamesLoader(void* obj
) {
1431 if (obj
== DUMMY_LOADER
) { return; }
1432 const ZNames::ZNamesLoader
* loader
= (const ZNames::ZNamesLoader
*) obj
;
1437 struct TimeZoneNamesImpl::ZoneStringsLoader
: public ResourceSink
{
1438 TimeZoneNamesImpl
& tzn
;
1439 UHashtable
* keyToLoader
;
1441 ZoneStringsLoader(TimeZoneNamesImpl
& _tzn
, UErrorCode
& status
)
1443 keyToLoader
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
1444 if (U_FAILURE(status
)) { return; }
1445 uhash_setKeyDeleter(keyToLoader
, uprv_free
);
1446 uhash_setValueDeleter(keyToLoader
, deleteZNamesLoader
);
1448 virtual ~ZoneStringsLoader();
1450 void* createKey(const char* key
, UErrorCode
& status
) {
1451 int32_t len
= sizeof(char) * (uprv_strlen(key
) + 1);
1452 char* newKey
= (char*) uprv_malloc(len
);
1453 if (newKey
== NULL
) {
1454 status
= U_MEMORY_ALLOCATION_ERROR
;
1457 uprv_memcpy(newKey
, key
, len
);
1458 newKey
[len
-1] = '\0';
1459 return (void*) newKey
;
1462 UBool
isMetaZone(const char* key
) {
1463 return (uprv_strlen(key
) >= MZ_PREFIX_LEN
&& uprv_memcmp(key
, gMZPrefix
, MZ_PREFIX_LEN
) == 0);
1466 UnicodeString
mzIDFromKey(const char* key
) {
1467 return UnicodeString(key
+ MZ_PREFIX_LEN
, uprv_strlen(key
) - MZ_PREFIX_LEN
, US_INV
);
1470 UnicodeString
tzIDFromKey(const char* key
) {
1471 UnicodeString
tzID(key
, -1, US_INV
);
1472 // Replace all colons ':' with slashes '/'
1473 for (int i
=0; i
<tzID
.length(); i
++) {
1474 if (tzID
.charAt(i
) == 0x003A) {
1475 tzID
.setCharAt(i
, 0x002F);
1481 void load(UErrorCode
& status
) {
1482 ures_getAllItemsWithFallback(tzn
.fZoneStrings
, "", *this, status
);
1483 if (U_FAILURE(status
)) { return; }
1485 int32_t pos
= UHASH_FIRST
;
1486 const UHashElement
* element
;
1487 while ((element
= uhash_nextElement(keyToLoader
, &pos
)) != NULL
) {
1488 if (element
->value
.pointer
== DUMMY_LOADER
) { continue; }
1489 ZNames::ZNamesLoader
* loader
= (ZNames::ZNamesLoader
*) element
->value
.pointer
;
1490 char* key
= (char*) element
->key
.pointer
;
1492 if (isMetaZone(key
)) {
1493 UnicodeString mzID
= mzIDFromKey(key
);
1494 ZNames::createMetaZoneAndPutInCache(tzn
.fMZNamesMap
, loader
->getNames(), mzID
, status
);
1496 UnicodeString tzID
= tzIDFromKey(key
);
1497 ZNames::createTimeZoneAndPutInCache(tzn
.fTZNamesMap
, loader
->getNames(), tzID
, status
);
1499 if (U_FAILURE(status
)) { return; }
1503 void consumeNamesTable(const char *key
, ResourceValue
&value
, UBool noFallback
,
1504 UErrorCode
&status
) {
1505 if (U_FAILURE(status
)) { return; }
1507 void* loader
= uhash_get(keyToLoader
, key
);
1508 if (loader
== NULL
) {
1509 if (isMetaZone(key
)) {
1510 UnicodeString mzID
= mzIDFromKey(key
);
1511 void* cacheVal
= uhash_get(tzn
.fMZNamesMap
, mzID
.getTerminatedBuffer());
1512 if (cacheVal
!= NULL
) {
1513 // We have already loaded the names for this meta zone.
1514 loader
= (void*) DUMMY_LOADER
;
1516 loader
= (void*) new ZNames::ZNamesLoader();
1517 if (loader
== NULL
) {
1518 status
= U_MEMORY_ALLOCATION_ERROR
;
1523 UnicodeString tzID
= tzIDFromKey(key
);
1524 void* cacheVal
= uhash_get(tzn
.fTZNamesMap
, tzID
.getTerminatedBuffer());
1525 if (cacheVal
!= NULL
) {
1526 // We have already loaded the names for this time zone.
1527 loader
= (void*) DUMMY_LOADER
;
1529 loader
= (void*) new ZNames::ZNamesLoader();
1530 if (loader
== NULL
) {
1531 status
= U_MEMORY_ALLOCATION_ERROR
;
1537 void* newKey
= createKey(key
, status
);
1538 if (U_FAILURE(status
)) {
1539 deleteZNamesLoader(loader
);
1543 uhash_put(keyToLoader
, newKey
, loader
, &status
);
1544 if (U_FAILURE(status
)) { return; }
1547 if (loader
!= DUMMY_LOADER
) {
1548 // Let the ZNamesLoader consume the names table.
1549 ((ZNames::ZNamesLoader
*)loader
)->put(key
, value
, noFallback
, status
);
1553 virtual void put(const char *key
, ResourceValue
&value
, UBool noFallback
,
1554 UErrorCode
&status
) {
1555 ResourceTable timeZonesTable
= value
.getTable(status
);
1556 if (U_FAILURE(status
)) { return; }
1557 for (int32_t i
= 0; timeZonesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
1558 U_ASSERT(!value
.isNoInheritanceMarker());
1559 if (value
.getType() == URES_TABLE
) {
1560 consumeNamesTable(key
, value
, noFallback
, status
);
1562 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1563 // All time zone fields are tables.
1565 if (U_FAILURE(status
)) { return; }
1570 // Virtual destructors must be defined out of line.
1571 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1572 uhash_close(keyToLoader
);
1575 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode
& status
) {
1576 if (U_FAILURE(status
)) return;
1579 Mutex
lock(&gDataMutex
);
1580 internalLoadAllDisplayNames(status
);
1584 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString
& tzID
,
1585 const UTimeZoneNameType types
[], int32_t numTypes
,
1586 UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
1587 if (U_FAILURE(status
)) return;
1589 if (tzID
.isEmpty()) { return; }
1590 void* tznames
= NULL
;
1591 void* mznames
= NULL
;
1592 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1594 // Load the time zone strings
1596 Mutex
lock(&gDataMutex
);
1597 tznames
= (void*) nonConstThis
->loadTimeZoneNames(tzID
, status
);
1598 if (U_FAILURE(status
)) { return; }
1600 U_ASSERT(tznames
!= NULL
);
1602 // Load the values into the dest array
1603 for (int i
= 0; i
< numTypes
; i
++) {
1604 UTimeZoneNameType type
= types
[i
];
1605 const UChar
* name
= ((ZNames
*)tznames
)->getName(type
);
1607 if (mznames
== NULL
) {
1608 // Load the meta zone name
1610 getMetaZoneID(tzID
, date
, mzID
);
1611 if (mzID
.isEmpty()) {
1612 mznames
= (void*) EMPTY
;
1614 // Load the meta zone strings
1615 // Mutex is scoped to the "else" statement
1616 Mutex
lock(&gDataMutex
);
1617 mznames
= (void*) nonConstThis
->loadMetaZoneNames(mzID
, status
);
1618 if (U_FAILURE(status
)) { return; }
1619 // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1620 // a dummy object instead of NULL.
1621 if (mznames
== NULL
) {
1622 mznames
= (void*) EMPTY
;
1626 U_ASSERT(mznames
!= NULL
);
1627 if (mznames
!= EMPTY
) {
1628 name
= ((ZNames
*)mznames
)->getName(type
);
1632 dest
[i
].setTo(TRUE
, name
, -1);
1634 dest
[i
].setToBogus();
1639 // Caller must synchronize.
1640 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode
& status
) {
1641 if (!fNamesFullyLoaded
) {
1642 fNamesFullyLoaded
= TRUE
;
1644 ZoneStringsLoader
loader(*this, status
);
1645 loader
.load(status
);
1646 if (U_FAILURE(status
)) { return; }
1648 const UnicodeString
*id
;
1650 // load strings for all zones
1651 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(
1652 UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1653 if (U_SUCCESS(status
)) {
1654 while ((id
= tzIDs
->snext(status
))) {
1655 if (U_FAILURE(status
)) {
1658 UnicodeString
copy(*id
);
1659 void* value
= uhash_get(fTZNamesMap
, copy
.getTerminatedBuffer());
1660 if (value
== NULL
) {
1661 // loadStrings also loads related metazone strings
1662 loadStrings(*id
, status
);
1666 if (tzIDs
!= NULL
) {
1674 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1675 static const int32_t gEtcPrefixLen
= 4;
1676 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1677 static const int32_t gSystemVPrefixLen
= 8;
1678 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1679 static const int32_t gRiyadh8Len
= 7;
1681 UnicodeString
& U_EXPORT2
1682 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1683 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1684 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1689 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1690 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1691 name
.setTo(tzID
, sep
+ 1);
1692 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1693 UnicodeString((UChar
)0x20 /* space */));
1700 // ---------------------------------------------------
1701 // TZDBTimeZoneNames and its supporting classes
1703 // TZDBTimeZoneNames is an implementation class of
1704 // TimeZoneNames holding the IANA tz database abbreviations.
1705 // ---------------------------------------------------
1707 class TZDBNames
: public UMemory
{
1709 virtual ~TZDBNames();
1711 static TZDBNames
* createInstance(UResourceBundle
* rb
, const char* key
);
1712 const UChar
* getName(UTimeZoneNameType type
) const;
1713 const char** getParseRegions(int32_t& numRegions
) const;
1716 TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
);
1719 const UChar
** fNames
;
1721 int32_t fNumRegions
;
1724 TZDBNames::TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
)
1727 fNumRegions(numRegions
) {
1730 TZDBNames::~TZDBNames() {
1731 if (fNames
!= NULL
) {
1734 if (fRegions
!= NULL
) {
1735 char **p
= fRegions
;
1736 for (int32_t i
= 0; i
< fNumRegions
; p
++, i
++) {
1739 uprv_free(fRegions
);
1744 TZDBNames::createInstance(UResourceBundle
* rb
, const char* key
) {
1745 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
1749 UErrorCode status
= U_ZERO_ERROR
;
1751 const UChar
**names
= NULL
;
1752 char** regions
= NULL
;
1753 int32_t numRegions
= 0;
1757 UResourceBundle
* rbTable
= NULL
;
1758 rbTable
= ures_getByKey(rb
, key
, rbTable
, &status
);
1759 if (U_FAILURE(status
)) {
1763 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * TZDBNAMES_KEYS_SIZE
);
1764 UBool isEmpty
= TRUE
;
1765 if (names
!= NULL
) {
1766 for (int32_t i
= 0; i
< TZDBNAMES_KEYS_SIZE
; i
++) {
1767 status
= U_ZERO_ERROR
;
1768 const UChar
*value
= ures_getStringByKey(rbTable
, TZDBNAMES_KEYS
[i
], &len
, &status
);
1769 if (U_FAILURE(status
) || len
== 0) {
1779 if (names
!= NULL
) {
1785 UResourceBundle
*regionsRes
= ures_getByKey(rbTable
, "parseRegions", NULL
, &status
);
1786 UBool regionError
= FALSE
;
1787 if (U_SUCCESS(status
)) {
1788 numRegions
= ures_getSize(regionsRes
);
1789 if (numRegions
> 0) {
1790 regions
= (char**)uprv_malloc(sizeof(char*) * numRegions
);
1791 if (regions
!= NULL
) {
1792 char **pRegion
= regions
;
1793 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1798 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1799 status
= U_ZERO_ERROR
;
1800 const UChar
*uregion
= ures_getStringByIndex(regionsRes
, i
, &len
, &status
);
1801 if (U_FAILURE(status
)) {
1805 *pRegion
= (char*)uprv_malloc(sizeof(char) * (len
+ 1));
1806 if (*pRegion
== NULL
) {
1810 u_UCharsToChars(uregion
, *pRegion
, len
);
1811 (*pRegion
)[len
] = 0;
1816 ures_close(regionsRes
);
1817 ures_close(rbTable
);
1820 if (names
!= NULL
) {
1823 if (regions
!= NULL
) {
1825 for (int32_t i
= 0; i
< numRegions
; p
++, i
++) {
1833 return new TZDBNames(names
, regions
, numRegions
);
1837 TZDBNames::getName(UTimeZoneNameType type
) const {
1838 if (fNames
== NULL
) {
1841 const UChar
*name
= NULL
;
1843 case UTZNM_SHORT_STANDARD
:
1846 case UTZNM_SHORT_DAYLIGHT
:
1856 TZDBNames::getParseRegions(int32_t& numRegions
) const {
1857 if (fRegions
== NULL
) {
1860 numRegions
= fNumRegions
;
1862 return (const char**)fRegions
;
1867 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1870 typedef struct TZDBNameInfo
{
1872 UTimeZoneNameType type
;
1873 UBool ambiguousType
;
1874 const char** parseRegions
;
1880 class TZDBNameSearchHandler
: public TextTrieMapSearchResultHandler
{
1882 TZDBNameSearchHandler(uint32_t types
, const char* region
);
1883 virtual ~TZDBNameSearchHandler();
1885 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
1886 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
1890 int32_t fMaxMatchLen
;
1891 TimeZoneNames::MatchInfoCollection
* fResults
;
1892 const char* fRegion
;
1895 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types
, const char* region
)
1896 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
), fRegion(region
) {
1899 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1900 if (fResults
!= NULL
) {
1906 TZDBNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
1907 if (U_FAILURE(status
)) {
1911 TZDBNameInfo
*match
= NULL
;
1912 TZDBNameInfo
*defaultRegionMatch
= NULL
;
1914 if (node
->hasValues()) {
1915 int32_t valuesCount
= node
->countValues();
1916 for (int32_t i
= 0; i
< valuesCount
; i
++) {
1917 TZDBNameInfo
*ninfo
= (TZDBNameInfo
*)node
->getValue(i
);
1918 if (ninfo
== NULL
) {
1921 if ((ninfo
->type
& fTypes
) != 0) {
1922 // Some tz database abbreviations are ambiguous. For example,
1923 // CST means either Central Standard Time or China Standard Time.
1924 // Unlike CLDR time zone display names, this implementation
1925 // does not use unique names. And TimeZoneFormat does not expect
1926 // multiple results returned for the same time zone type.
1927 // For this reason, this implementation resolve one among same
1928 // zone type with a same name at this level.
1929 if (ninfo
->parseRegions
== NULL
) {
1930 // parseRegions == null means this is the default metazone
1931 // mapping for the abbreviation.
1932 if (defaultRegionMatch
== NULL
) {
1933 match
= defaultRegionMatch
= ninfo
;
1936 UBool matchRegion
= FALSE
;
1937 // non-default metazone mapping for an abbreviation
1938 // comes with applicable regions. For example, the default
1939 // metazone mapping for "CST" is America_Central,
1940 // but if region is one of CN/MO/TW, "CST" is parsed
1941 // as metazone China (China Standard Time).
1942 for (int32_t i
= 0; i
< ninfo
->nRegions
; i
++) {
1943 const char *region
= ninfo
->parseRegions
[i
];
1944 if (uprv_strcmp(fRegion
, region
) == 0) {
1953 if (match
== NULL
) {
1960 if (match
!= NULL
) {
1961 UTimeZoneNameType ntype
= match
->type
;
1962 // Note: Workaround for duplicated standard/daylight names
1963 // The tz database contains a few zones sharing a
1964 // same name for both standard time and daylight saving
1965 // time. For example, Australia/Sydney observes DST,
1966 // but "EST" is used for both standard and daylight.
1967 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1968 // in the find operation, we cannot tell which one was
1969 // actually matched.
1970 // TimeZoneFormat#parse returns a matched name type (standard
1971 // or daylight) and DateFormat implementation uses the info to
1972 // to adjust actual time. To avoid false type information,
1973 // this implementation replaces the name type with SHORT_GENERIC.
1974 if (match
->ambiguousType
1975 && (ntype
== UTZNM_SHORT_STANDARD
|| ntype
== UTZNM_SHORT_DAYLIGHT
)
1976 && (fTypes
& UTZNM_SHORT_STANDARD
) != 0
1977 && (fTypes
& UTZNM_SHORT_DAYLIGHT
) != 0) {
1978 ntype
= UTZNM_SHORT_GENERIC
;
1981 if (fResults
== NULL
) {
1982 fResults
= new TimeZoneNames::MatchInfoCollection();
1983 if (fResults
== NULL
) {
1984 status
= U_MEMORY_ALLOCATION_ERROR
;
1987 if (U_SUCCESS(status
)) {
1988 U_ASSERT(fResults
!= NULL
);
1989 U_ASSERT(match
->mzID
!= NULL
);
1990 fResults
->addMetaZone(ntype
, matchLength
, UnicodeString(match
->mzID
, -1), status
);
1991 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
1992 fMaxMatchLen
= matchLength
;
2000 TimeZoneNames::MatchInfoCollection
*
2001 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
2002 // give the ownership to the caller
2003 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
2004 maxMatchLen
= fMaxMatchLen
;
2014 * Deleter for TZDBNames
2016 static void U_CALLCONV
2017 deleteTZDBNames(void *obj
) {
2019 delete (TZDBNames
*)obj
;
2023 static void U_CALLCONV
initTZDBNamesMap(UErrorCode
&status
) {
2024 gTZDBNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2025 if (U_FAILURE(status
)) {
2026 gTZDBNamesMap
= NULL
;
2029 // no key deleters for tzdb name maps
2030 uhash_setValueDeleter(gTZDBNamesMap
, deleteTZDBNames
);
2031 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2035 * Deleter for TZDBNameInfo
2037 static void U_CALLCONV
2038 deleteTZDBNameInfo(void *obj
) {
2044 static void U_CALLCONV
prepareFind(UErrorCode
&status
) {
2045 if (U_FAILURE(status
)) {
2048 gTZDBNamesTrie
= new TextTrieMap(TRUE
, deleteTZDBNameInfo
);
2049 if (gTZDBNamesTrie
== NULL
) {
2050 status
= U_MEMORY_ALLOCATION_ERROR
;
2054 const UnicodeString
*mzID
;
2055 StringEnumeration
*mzIDs
= TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2056 if (U_SUCCESS(status
)) {
2057 while ((mzID
= mzIDs
->snext(status
)) && U_SUCCESS(status
)) {
2058 const TZDBNames
*names
= TZDBTimeZoneNames::getMetaZoneNames(*mzID
, status
);
2059 if (names
== NULL
) {
2062 const UChar
*std
= names
->getName(UTZNM_SHORT_STANDARD
);
2063 const UChar
*dst
= names
->getName(UTZNM_SHORT_DAYLIGHT
);
2064 if (std
== NULL
&& dst
== NULL
) {
2067 int32_t numRegions
= 0;
2068 const char **parseRegions
= names
->getParseRegions(numRegions
);
2070 // The tz database contains a few zones sharing a
2071 // same name for both standard time and daylight saving
2072 // time. For example, Australia/Sydney observes DST,
2073 // but "EST" is used for both standard and daylight.
2074 // we need to store the information for later processing.
2075 UBool ambiguousType
= (std
!= NULL
&& dst
!= NULL
&& u_strcmp(std
, dst
) == 0);
2077 const UChar
*uMzID
= ZoneMeta::findMetaZoneID(*mzID
);
2079 TZDBNameInfo
*stdInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2080 if (stdInf
== NULL
) {
2081 status
= U_MEMORY_ALLOCATION_ERROR
;
2084 stdInf
->mzID
= uMzID
;
2085 stdInf
->type
= UTZNM_SHORT_STANDARD
;
2086 stdInf
->ambiguousType
= ambiguousType
;
2087 stdInf
->parseRegions
= parseRegions
;
2088 stdInf
->nRegions
= numRegions
;
2089 gTZDBNamesTrie
->put(std
, stdInf
, status
);
2091 if (U_SUCCESS(status
) && dst
!= NULL
) {
2092 TZDBNameInfo
*dstInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
2093 if (dstInf
== NULL
) {
2094 status
= U_MEMORY_ALLOCATION_ERROR
;
2097 dstInf
->mzID
= uMzID
;
2098 dstInf
->type
= UTZNM_SHORT_DAYLIGHT
;
2099 dstInf
->ambiguousType
= ambiguousType
;
2100 dstInf
->parseRegions
= parseRegions
;
2101 dstInf
->nRegions
= numRegions
;
2102 gTZDBNamesTrie
->put(dst
, dstInf
, status
);
2108 if (U_FAILURE(status
)) {
2109 delete gTZDBNamesTrie
;
2110 gTZDBNamesTrie
= NULL
;
2114 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
2119 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale
& locale
)
2121 UBool useWorld
= TRUE
;
2122 const char* region
= fLocale
.getCountry();
2123 int32_t regionLen
= uprv_strlen(region
);
2124 if (regionLen
== 0) {
2125 UErrorCode status
= U_ZERO_ERROR
;
2126 char loc
[ULOC_FULLNAME_CAPACITY
];
2127 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
2128 regionLen
= uloc_getCountry(loc
, fRegion
, sizeof(fRegion
), &status
);
2129 if (U_SUCCESS(status
) && regionLen
< (int32_t)sizeof(fRegion
)) {
2132 } else if (regionLen
< (int32_t)sizeof(fRegion
)) {
2133 uprv_strcpy(fRegion
, region
);
2137 uprv_strcpy(fRegion
, "001");
2141 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2145 TZDBTimeZoneNames::operator==(const TimeZoneNames
& other
) const {
2146 if (this == &other
) {
2149 // No implementation for now
2154 TZDBTimeZoneNames::clone() const {
2155 return new TZDBTimeZoneNames(fLocale
);
2159 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
2160 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
2164 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
2165 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
2169 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
2170 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
2174 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
2175 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
2179 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString
& mzID
,
2180 UTimeZoneNameType type
,
2181 UnicodeString
& name
) const {
2183 if (mzID
.isEmpty()) {
2187 UErrorCode status
= U_ZERO_ERROR
;
2188 const TZDBNames
*tzdbNames
= TZDBTimeZoneNames::getMetaZoneNames(mzID
, status
);
2189 if (U_SUCCESS(status
)) {
2190 const UChar
*s
= tzdbNames
->getName(type
);
2192 name
.setTo(TRUE
, s
, -1);
2200 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString
& /* tzID */, UTimeZoneNameType
/* type */, UnicodeString
& name
) const {
2201 // No abbreviations associated a zone directly for now.
2206 TZDBTimeZoneNames::MatchInfoCollection
*
2207 TZDBTimeZoneNames::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
2208 umtx_initOnce(gTZDBNamesTrieInitOnce
, &prepareFind
, status
);
2209 if (U_FAILURE(status
)) {
2213 TZDBNameSearchHandler
handler(types
, fRegion
);
2214 gTZDBNamesTrie
->search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
2215 if (U_FAILURE(status
)) {
2219 return handler
.getMatches(maxLen
);
2223 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
2224 umtx_initOnce(gTZDBNamesMapInitOnce
, &initTZDBNamesMap
, status
);
2225 if (U_FAILURE(status
)) {
2229 TZDBNames
* tzdbNames
= NULL
;
2231 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
2232 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
2233 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
2234 mzIDKey
[mzID
.length()] = 0;
2236 umtx_lock(&gTZDBNamesMapLock
);
2238 void *cacheVal
= uhash_get(gTZDBNamesMap
, mzIDKey
);
2239 if (cacheVal
== NULL
) {
2240 UResourceBundle
*zoneStringsRes
= ures_openDirect(U_ICUDATA_ZONE
, "tzdbNames", &status
);
2241 zoneStringsRes
= ures_getByKey(zoneStringsRes
, gZoneStrings
, zoneStringsRes
, &status
);
2242 if (U_SUCCESS(status
)) {
2243 char key
[ZID_KEY_MAX
+ 1];
2244 mergeTimeZoneKey(mzID
, key
);
2245 tzdbNames
= TZDBNames::createInstance(zoneStringsRes
, key
);
2247 if (tzdbNames
== NULL
) {
2248 cacheVal
= (void *)EMPTY
;
2250 cacheVal
= tzdbNames
;
2252 // Use the persistent ID as the resource key, so we can
2253 // avoid duplications.
2254 // TODO: Is there a more efficient way, like intern() in Java?
2255 void* newKey
= (void*) ZoneMeta::findMetaZoneID(mzID
);
2256 if (newKey
!= NULL
) {
2257 uhash_put(gTZDBNamesMap
, newKey
, cacheVal
, &status
);
2258 if (U_FAILURE(status
)) {
2259 if (tzdbNames
!= NULL
) {
2265 // Should never happen with a valid input
2266 if (tzdbNames
!= NULL
) {
2267 // It's not possible that we get a valid tzdbNames with unknown ID.
2268 // But just in case..
2274 ures_close(zoneStringsRes
);
2275 } else if (cacheVal
!= EMPTY
) {
2276 tzdbNames
= (TZDBNames
*)cacheVal
;
2279 umtx_unlock(&gTZDBNamesMapLock
);
2287 #endif /* #if !UCONFIG_NO_FORMATTING */