2 *******************************************************************************
3 * Copyright (C) 2011-2016, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
7 * File TZNAMES_IMPL.CPP
9 *******************************************************************************
12 #include "unicode/utypes.h"
14 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/ustring.h"
17 #include "unicode/timezone.h"
19 #include "tznames_impl.h"
34 #define ZID_KEY_MAX 128
35 #define MZ_PREFIX_LEN 5
37 static const char gZoneStrings
[] = "zoneStrings";
38 static const char gMZPrefix
[] = "meta:";
40 static const char* KEYS
[] = {"lg", "ls", "ld", "sg", "ss", "sd"};
41 static const int32_t KEYS_SIZE
= UPRV_LENGTHOF(KEYS
);
43 static const char gEcTag
[] = "ec";
45 static const char EMPTY
[] = "<empty>"; // place holder for empty ZNames/TZNames
47 static const UTimeZoneNameType ALL_NAME_TYPES
[] = {
48 UTZNM_LONG_GENERIC
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
,
49 UTZNM_SHORT_GENERIC
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
,
50 UTZNM_EXEMPLAR_LOCATION
,
51 UTZNM_UNKNOWN
// unknown as the last one
54 // stuff for TZDBTimeZoneNames
55 static const char* TZDBNAMES_KEYS
[] = {"ss", "sd"};
56 static const int32_t TZDBNAMES_KEYS_SIZE
= UPRV_LENGTHOF(TZDBNAMES_KEYS
);
58 static UMutex gTZDBNamesMapLock
= U_MUTEX_INITIALIZER
;
60 static UHashtable
* gTZDBNamesMap
= NULL
;
61 static icu::UInitOnce gTZDBNamesMapInitOnce
= U_INITONCE_INITIALIZER
;
63 static TextTrieMap
* gTZDBNamesTrie
= NULL
;
64 static icu::UInitOnce gTZDBNamesTrieInitOnce
= U_INITONCE_INITIALIZER
;
67 static UBool U_CALLCONV
tzdbTimeZoneNames_cleanup(void) {
68 if (gTZDBNamesMap
!= NULL
) {
69 uhash_close(gTZDBNamesMap
);
72 gTZDBNamesMapInitOnce
.reset();
74 if (gTZDBNamesTrie
!= NULL
) {
75 delete gTZDBNamesTrie
;
76 gTZDBNamesTrie
= NULL
;
78 gTZDBNamesTrieInitOnce
.reset();
84 #define DEFAULT_CHARACTERNODE_CAPACITY 1
86 // ---------------------------------------------------
87 // CharacterNode class implementation
88 // ---------------------------------------------------
89 void CharacterNode::clear() {
90 uprv_memset(this, 0, sizeof(*this));
93 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
94 if (fValues
== NULL
) {
96 } else if (!fHasValuesVector
) {
98 valueDeleter(fValues
);
101 delete (UVector
*)fValues
;
106 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
107 if (U_FAILURE(status
)) {
113 if (fValues
== NULL
) {
116 // At least one value already.
117 if (!fHasValuesVector
) {
118 // There is only one value so far, and not in a vector yet.
119 // Create a vector and add the old value.
120 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
121 if (U_FAILURE(status
)) {
127 values
->addElement(fValues
, status
);
129 fHasValuesVector
= TRUE
;
131 // Add the new value.
132 ((UVector
*)fValues
)->addElement(value
, status
);
136 // ---------------------------------------------------
137 // TextTrieMapSearchResultHandler class implementation
138 // ---------------------------------------------------
139 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
142 // ---------------------------------------------------
143 // TextTrieMap class implementation
144 // ---------------------------------------------------
145 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
146 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
147 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
150 TextTrieMap::~TextTrieMap() {
152 for (index
= 0; index
< fNodesCount
; ++index
) {
153 fNodes
[index
].deleteValues(fValueDeleter
);
156 if (fLazyContents
!= NULL
) {
157 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
159 fValueDeleter(fLazyContents
->elementAt(i
+1));
162 delete fLazyContents
;
166 int32_t TextTrieMap::isEmpty() const {
167 // Use a separate field for fIsEmpty because it will remain unchanged once the
168 // Trie is built, while fNodes and fLazyContents change with the lazy init
169 // of the nodes structure. Trying to test the changing fields has
170 // thread safety complications.
175 // We defer actually building the TextTrieMap node structure until the first time a
176 // search is performed. put() simply saves the parameters in case we do
177 // eventually need to build it.
180 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
181 const UChar
*s
= sp
.get(key
, status
);
182 put(s
, value
, status
);
185 // This method is for designed for a persistent key, such as string key stored in
188 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
190 if (fLazyContents
== NULL
) {
191 fLazyContents
= new UVector(status
);
192 if (fLazyContents
== NULL
) {
193 status
= U_MEMORY_ALLOCATION_ERROR
;
196 if (U_FAILURE(status
)) {
199 U_ASSERT(fLazyContents
!= NULL
);
200 UChar
*s
= const_cast<UChar
*>(key
);
201 fLazyContents
->addElement(s
, status
);
202 fLazyContents
->addElement(value
, status
);
206 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
207 if (fNodes
== NULL
) {
208 fNodesCapacity
= 512;
209 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
210 fNodes
[0].clear(); // Init root node.
214 UnicodeString foldedKey
;
215 const UChar
*keyBuffer
;
218 // Ok to use fastCopyFrom() because we discard the copy when we return.
219 foldedKey
.fastCopyFrom(key
).foldCase();
220 keyBuffer
= foldedKey
.getBuffer();
221 keyLength
= foldedKey
.length();
223 keyBuffer
= key
.getBuffer();
224 keyLength
= key
.length();
227 CharacterNode
*node
= fNodes
;
229 for (index
= 0; index
< keyLength
; ++index
) {
230 node
= addChildNode(node
, keyBuffer
[index
], status
);
232 node
->addValue(value
, fValueDeleter
, status
);
236 TextTrieMap::growNodes() {
237 if (fNodesCapacity
== 0xffff) {
238 return FALSE
; // We use 16-bit node indexes.
240 int32_t newCapacity
= fNodesCapacity
+ 1000;
241 if (newCapacity
> 0xffff) {
242 newCapacity
= 0xffff;
244 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
245 if (newNodes
== NULL
) {
248 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
251 fNodesCapacity
= newCapacity
;
256 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
257 if (U_FAILURE(status
)) {
260 // Linear search of the sorted list of children.
261 uint16_t prevIndex
= 0;
262 uint16_t nodeIndex
= parent
->fFirstChild
;
263 while (nodeIndex
> 0) {
264 CharacterNode
*current
= fNodes
+ nodeIndex
;
265 UChar childCharacter
= current
->fCharacter
;
266 if (childCharacter
== c
) {
268 } else if (childCharacter
> c
) {
271 prevIndex
= nodeIndex
;
272 nodeIndex
= current
->fNextSibling
;
275 // Ensure capacity. Grow fNodes[] if needed.
276 if (fNodesCount
== fNodesCapacity
) {
277 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
279 status
= U_MEMORY_ALLOCATION_ERROR
;
282 parent
= fNodes
+ parentIndex
;
285 // Insert a new child node with c in sorted order.
286 CharacterNode
*node
= fNodes
+ fNodesCount
;
288 node
->fCharacter
= c
;
289 node
->fNextSibling
= nodeIndex
;
290 if (prevIndex
== 0) {
291 parent
->fFirstChild
= (uint16_t)fNodesCount
;
293 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
300 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
301 // Linear search of the sorted list of children.
302 uint16_t nodeIndex
= parent
->fFirstChild
;
303 while (nodeIndex
> 0) {
304 CharacterNode
*current
= fNodes
+ nodeIndex
;
305 UChar childCharacter
= current
->fCharacter
;
306 if (childCharacter
== c
) {
308 } else if (childCharacter
> c
) {
311 nodeIndex
= current
->fNextSibling
;
316 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
317 static UMutex TextTrieMutex
= U_MUTEX_INITIALIZER
;
319 // buildTrie() - The Trie node structure is needed. Create it from the data that was
320 // saved at the time the ZoneStringFormatter was created. The Trie is only
321 // needed for parsing operations, which are less common than formatting,
322 // and the Trie is big, which is why its creation is deferred until first use.
323 void TextTrieMap::buildTrie(UErrorCode
&status
) {
324 if (fLazyContents
!= NULL
) {
325 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
326 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
327 void *val
= fLazyContents
->elementAt(i
+1);
328 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
329 putImpl(keyString
, val
, status
);
331 delete fLazyContents
;
332 fLazyContents
= NULL
;
337 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
338 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
340 // TODO: if locking the mutex for each check proves to be a performance problem,
341 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
342 // the ICU atomic safe functions for assigning and testing.
343 // Don't test the pointer fLazyContents.
344 // Don't do unless it's really required.
345 Mutex
lock(&TextTrieMutex
);
346 if (fLazyContents
!= NULL
) {
347 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
348 nonConstThis
->buildTrie(status
);
351 if (fNodes
== NULL
) {
354 search(fNodes
, text
, start
, start
, handler
, status
);
358 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
359 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
360 if (U_FAILURE(status
)) {
363 if (node
->hasValues()) {
364 if (!handler
->handleMatch(index
- start
, node
, status
)) {
367 if (U_FAILURE(status
)) {
371 UChar32 c
= text
.char32At(index
);
373 // size of character may grow after fold operation
374 UnicodeString
tmp(c
);
377 while (tmpidx
< tmp
.length()) {
378 c
= tmp
.char32At(tmpidx
);
379 node
= getChildNode(node
, c
);
383 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
386 node
= getChildNode(node
, c
);
389 search(node
, text
, start
, index
+1, handler
, status
);
393 // ---------------------------------------------------
394 // ZNStringPool class implementation
395 // ---------------------------------------------------
396 static const int32_t POOL_CHUNK_SIZE
= 2000;
397 struct ZNStringPoolChunk
: public UMemory
{
398 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
399 int32_t fLimit
; // Index to start of unused area at end of fStrings
400 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
404 ZNStringPoolChunk::ZNStringPoolChunk() {
409 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
412 if (U_FAILURE(status
)) {
415 fChunks
= new ZNStringPoolChunk
;
416 if (fChunks
== NULL
) {
417 status
= U_MEMORY_ALLOCATION_ERROR
;
421 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
422 uhash_compareUChars
/* keyComp */,
423 uhash_compareUChars
/* valueComp */,
425 if (U_FAILURE(status
)) {
430 ZNStringPool::~ZNStringPool() {
436 while (fChunks
!= NULL
) {
437 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
443 static const UChar EmptyString
= 0;
445 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
446 const UChar
*pooledString
;
447 if (U_FAILURE(status
)) {
451 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
452 if (pooledString
!= NULL
) {
456 int32_t length
= u_strlen(s
);
457 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
458 if (remainingLength
<= length
) {
459 U_ASSERT(length
< POOL_CHUNK_SIZE
);
460 if (length
>= POOL_CHUNK_SIZE
) {
461 status
= U_INTERNAL_PROGRAM_ERROR
;
464 ZNStringPoolChunk
*oldChunk
= fChunks
;
465 fChunks
= new ZNStringPoolChunk
;
466 if (fChunks
== NULL
) {
467 status
= U_MEMORY_ALLOCATION_ERROR
;
470 fChunks
->fNext
= oldChunk
;
473 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
474 u_strcpy(destString
, s
);
475 fChunks
->fLimit
+= (length
+ 1);
476 uhash_put(fHash
, destString
, destString
, &status
);
482 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
483 // into the pool's storage. Used for strings from resource bundles,
484 // which will perisist for the life of the zone string formatter, and
485 // therefore can be used directly without copying.
486 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
487 const UChar
*pooledString
;
488 if (U_FAILURE(status
)) {
492 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
493 if (pooledString
== NULL
) {
494 UChar
*ncs
= const_cast<UChar
*>(s
);
495 uhash_put(fHash
, ncs
, ncs
, &status
);
502 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
503 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
504 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
508 * freeze(). Close the hash table that maps to the pooled strings.
509 * After freezing, the pool can not be searched or added to,
510 * but all existing references to pooled strings remain valid.
512 * The main purpose is to recover the storage used for the hash.
514 void ZNStringPool::freeze() {
520 // ---------------------------------------------------
521 // ZNames - names common for time zone and meta zone
522 // ---------------------------------------------------
523 class ZNames
: public UMemory
{
527 static ZNames
* createInstance(UResourceBundle
* rb
, const char* key
);
528 virtual const UChar
* getName(UTimeZoneNameType type
);
531 ZNames(const UChar
** names
);
532 static const UChar
** loadData(UResourceBundle
* rb
, const char* key
);
535 const UChar
** fNames
;
538 ZNames::ZNames(const UChar
** names
)
543 if (fNames
!= NULL
) {
549 ZNames::createInstance(UResourceBundle
* rb
, const char* key
) {
550 const UChar
** names
= loadData(rb
, key
);
552 // No names data available
555 return new ZNames(names
);
559 ZNames::getName(UTimeZoneNameType type
) {
560 if (fNames
== NULL
) {
563 const UChar
*name
= NULL
;
565 case UTZNM_LONG_GENERIC
:
568 case UTZNM_LONG_STANDARD
:
571 case UTZNM_LONG_DAYLIGHT
:
574 case UTZNM_SHORT_GENERIC
:
577 case UTZNM_SHORT_STANDARD
:
580 case UTZNM_SHORT_DAYLIGHT
:
583 case UTZNM_EXEMPLAR_LOCATION
: // implemeted by subclass
591 ZNames::loadData(UResourceBundle
* rb
, const char* key
) {
592 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
596 UErrorCode status
= U_ZERO_ERROR
;
597 const UChar
**names
= NULL
;
599 UResourceBundle
* rbTable
= NULL
;
600 rbTable
= ures_getByKeyWithFallback(rb
, key
, rbTable
, &status
);
601 if (U_SUCCESS(status
)) {
602 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * KEYS_SIZE
);
604 UBool isEmpty
= TRUE
;
605 for (int32_t i
= 0; i
< KEYS_SIZE
; i
++) {
606 status
= U_ZERO_ERROR
;
608 const UChar
*value
= ures_getStringByKeyWithFallback(rbTable
, KEYS
[i
], &len
, &status
);
609 if (U_FAILURE(status
) || len
== 0) {
617 // No need to keep the names array
627 // ---------------------------------------------------
628 // TZNames - names for a time zone
629 // ---------------------------------------------------
630 class TZNames
: public ZNames
{
634 static TZNames
* createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
);
635 virtual const UChar
* getName(UTimeZoneNameType type
);
638 TZNames(const UChar
** names
);
639 const UChar
* fLocationName
;
640 UChar
* fLocationNameOwned
;
643 TZNames::TZNames(const UChar
** names
)
644 : ZNames(names
), fLocationName(NULL
), fLocationNameOwned(NULL
) {
647 TZNames::~TZNames() {
648 if (fLocationNameOwned
) {
649 uprv_free(fLocationNameOwned
);
654 TZNames::getName(UTimeZoneNameType type
) {
655 if (type
== UTZNM_EXEMPLAR_LOCATION
) {
656 return fLocationName
;
658 return ZNames::getName(type
);
662 TZNames::createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
) {
663 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
667 const UChar
** names
= loadData(rb
, key
);
668 const UChar
* locationName
= NULL
;
669 UChar
* locationNameOwned
= NULL
;
671 UErrorCode status
= U_ZERO_ERROR
;
674 UResourceBundle
* table
= ures_getByKeyWithFallback(rb
, key
, NULL
, &status
);
675 locationName
= ures_getStringByKeyWithFallback(table
, gEcTag
, &len
, &status
);
676 // ignore missing resource here
677 status
= U_ZERO_ERROR
;
681 if (locationName
== NULL
) {
682 UnicodeString tmpName
;
683 int32_t tmpNameLen
= 0;
684 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, tmpName
);
685 tmpNameLen
= tmpName
.length();
687 if (tmpNameLen
> 0) {
688 locationNameOwned
= (UChar
*) uprv_malloc(sizeof(UChar
) * (tmpNameLen
+ 1));
689 if (locationNameOwned
) {
690 tmpName
.extract(locationNameOwned
, tmpNameLen
+ 1, status
);
691 locationName
= locationNameOwned
;
696 TZNames
* tznames
= NULL
;
697 if (locationName
!= NULL
|| names
!= NULL
) {
698 tznames
= new TZNames(names
);
699 if (tznames
== NULL
) {
700 if (locationNameOwned
) {
701 uprv_free(locationNameOwned
);
704 tznames
->fLocationName
= locationName
;
705 tznames
->fLocationNameOwned
= locationNameOwned
;
711 // ---------------------------------------------------
712 // The meta zone ID enumeration class
713 // ---------------------------------------------------
714 class MetaZoneIDsEnumeration
: public StringEnumeration
{
716 MetaZoneIDsEnumeration();
717 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
718 MetaZoneIDsEnumeration(UVector
* mzIDs
);
719 virtual ~MetaZoneIDsEnumeration();
720 static UClassID U_EXPORT2
getStaticClassID(void);
721 virtual UClassID
getDynamicClassID(void) const;
722 virtual const UnicodeString
* snext(UErrorCode
& status
);
723 virtual void reset(UErrorCode
& status
);
724 virtual int32_t count(UErrorCode
& status
) const;
728 const UVector
* fMetaZoneIDs
;
729 UVector
*fLocalVector
;
732 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
734 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
735 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
738 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
739 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
740 fLen
= fMetaZoneIDs
->size();
743 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
744 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
746 fLen
= fMetaZoneIDs
->size();
751 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
752 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
753 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
760 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
765 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
769 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
777 * ZNameInfo stores zone name information in the trie
779 typedef struct ZNameInfo
{
780 UTimeZoneNameType type
;
786 * ZMatchInfo stores zone name match information used by find method
788 typedef struct ZMatchInfo
{
789 const ZNameInfo
* znameInfo
;
795 // ---------------------------------------------------
796 // ZNameSearchHandler
797 // ---------------------------------------------------
798 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
800 ZNameSearchHandler(uint32_t types
);
801 virtual ~ZNameSearchHandler();
803 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
804 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
808 int32_t fMaxMatchLen
;
809 TimeZoneNames::MatchInfoCollection
* fResults
;
812 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
813 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
816 ZNameSearchHandler::~ZNameSearchHandler() {
817 if (fResults
!= NULL
) {
823 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
824 if (U_FAILURE(status
)) {
827 if (node
->hasValues()) {
828 int32_t valuesCount
= node
->countValues();
829 for (int32_t i
= 0; i
< valuesCount
; i
++) {
830 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
831 if (nameinfo
== NULL
) {
834 if ((nameinfo
->type
& fTypes
) != 0) {
835 // matches a requested type
836 if (fResults
== NULL
) {
837 fResults
= new TimeZoneNames::MatchInfoCollection();
838 if (fResults
== NULL
) {
839 status
= U_MEMORY_ALLOCATION_ERROR
;
842 if (U_SUCCESS(status
)) {
843 U_ASSERT(fResults
!= NULL
);
844 if (nameinfo
->tzID
) {
845 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
847 U_ASSERT(nameinfo
->mzID
);
848 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
850 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
851 fMaxMatchLen
= matchLength
;
860 TimeZoneNames::MatchInfoCollection
*
861 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
862 // give the ownership to the caller
863 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
864 maxMatchLen
= fMaxMatchLen
;
872 // ---------------------------------------------------
875 // TimeZoneNames implementation class. This is the main
876 // part of this module.
877 // ---------------------------------------------------
883 static void U_CALLCONV
884 deleteZNames(void *obj
) {
886 delete (ZNames
*)obj
;
890 * Deleter for TZNames
892 static void U_CALLCONV
893 deleteTZNames(void *obj
) {
895 delete (TZNames
*)obj
;
900 * Deleter for ZNameInfo
902 static void U_CALLCONV
903 deleteZNameInfo(void *obj
) {
909 static UMutex gLock
= U_MUTEX_INITIALIZER
;
911 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
916 fNamesTrieFullyLoaded(FALSE
),
917 fNamesTrie(TRUE
, deleteZNameInfo
) {
918 initialize(locale
, status
);
922 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
923 if (U_FAILURE(status
)) {
927 // Load zoneStrings bundle
928 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
929 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
930 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
931 if (U_FAILURE(tmpsts
)) {
937 // Initialize hashtables holding time zone/meta zone names
938 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
939 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
940 if (U_FAILURE(status
)) {
945 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
946 uhash_setValueDeleter(fTZNamesMap
, deleteTZNames
);
947 // no key deleters for name maps
949 // preload zone strings for the default zone
950 TimeZone
*tz
= TimeZone::createDefault();
951 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
953 loadStrings(UnicodeString(tzID
));
961 * This method updates the cache and must be called with a lock,
962 * except initializer.
965 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
) {
966 loadTimeZoneNames(tzCanonicalID
);
968 UErrorCode status
= U_ZERO_ERROR
;
969 StringEnumeration
*mzIDs
= getAvailableMetaZoneIDs(tzCanonicalID
, status
);
970 if (U_SUCCESS(status
) && mzIDs
!= NULL
) {
971 const UnicodeString
*mzID
;
972 while ((mzID
= mzIDs
->snext(status
))) {
973 if (U_FAILURE(status
)) {
976 loadMetaZoneNames(*mzID
);
982 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
987 TimeZoneNamesImpl::cleanup() {
988 if (fZoneStrings
!= NULL
) {
989 ures_close(fZoneStrings
);
992 if (fMZNamesMap
!= NULL
) {
993 uhash_close(fMZNamesMap
);
996 if (fTZNamesMap
!= NULL
) {
997 uhash_close(fTZNamesMap
);
1003 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
1004 if (this == &other
) {
1007 // No implementation for now
1012 TimeZoneNamesImpl::clone() const {
1013 UErrorCode status
= U_ZERO_ERROR
;
1014 return new TimeZoneNamesImpl(fLocale
, status
);
1018 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
1019 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1022 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1024 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode
& status
) {
1025 if (U_FAILURE(status
)) {
1028 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
1029 if (mzIDs
== NULL
) {
1030 return new MetaZoneIDsEnumeration();
1032 return new MetaZoneIDsEnumeration(*mzIDs
);
1036 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1037 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
1040 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1042 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) {
1043 if (U_FAILURE(status
)) {
1046 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1047 if (mappings
== NULL
) {
1048 return new MetaZoneIDsEnumeration();
1051 MetaZoneIDsEnumeration
*senum
= NULL
;
1052 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1053 if (mzIDs
== NULL
) {
1054 status
= U_MEMORY_ALLOCATION_ERROR
;
1056 if (U_SUCCESS(status
)) {
1057 U_ASSERT(mzIDs
!= NULL
);
1058 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1060 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1061 const UChar
*mzID
= map
->mzid
;
1062 if (!mzIDs
->contains((void *)mzID
)) {
1063 mzIDs
->addElement((void *)mzID
, status
);
1066 if (U_SUCCESS(status
)) {
1067 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1076 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1077 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
1080 // static implementation of getMetaZoneID
1082 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) {
1083 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1088 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1089 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
1092 // static implementaion of getReferenceZoneID
1094 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) {
1095 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1101 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1102 UTimeZoneNameType type
,
1103 UnicodeString
& name
) const {
1104 name
.setToBogus(); // cleanup result.
1105 if (mzID
.isEmpty()) {
1109 ZNames
*znames
= NULL
;
1110 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1114 znames
= nonConstThis
->loadMetaZoneNames(mzID
);
1116 umtx_unlock(&gLock
);
1118 if (znames
!= NULL
) {
1119 const UChar
* s
= znames
->getName(type
);
1121 name
.setTo(TRUE
, s
, -1);
1128 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1129 name
.setToBogus(); // cleanup result.
1130 if (tzID
.isEmpty()) {
1134 TZNames
*tznames
= NULL
;
1135 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1139 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1141 umtx_unlock(&gLock
);
1143 if (tznames
!= NULL
) {
1144 const UChar
*s
= tznames
->getName(type
);
1146 name
.setTo(TRUE
, s
, -1);
1153 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1154 name
.setToBogus(); // cleanup result.
1155 const UChar
* locName
= NULL
;
1156 TZNames
*tznames
= NULL
;
1157 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1161 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1163 umtx_unlock(&gLock
);
1165 if (tznames
!= NULL
) {
1166 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1168 if (locName
!= NULL
) {
1169 name
.setTo(TRUE
, locName
, -1);
1176 // Merge the MZ_PREFIX and mzId
1177 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1178 if (mzID
.isEmpty()) {
1183 char mzIdChar
[ZID_KEY_MAX
+ 1];
1185 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1186 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1187 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1188 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1189 result
[keyLen
+ prefixLen
] = '\0';
1193 * This method updates the cache and must be called with a lock
1196 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
) {
1197 if (mzID
.length() > (ZID_KEY_MAX
- MZ_PREFIX_LEN
)) {
1201 ZNames
*znames
= NULL
;
1203 UErrorCode status
= U_ZERO_ERROR
;
1204 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1205 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1206 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1207 mzIDKey
[mzID
.length()] = 0;
1209 void *cacheVal
= uhash_get(fMZNamesMap
, mzIDKey
);
1210 if (cacheVal
== NULL
) {
1211 char key
[ZID_KEY_MAX
+ 1];
1212 mergeTimeZoneKey(mzID
, key
);
1213 znames
= ZNames::createInstance(fZoneStrings
, key
);
1215 if (znames
== NULL
) {
1216 cacheVal
= (void *)EMPTY
;
1220 // Use the persistent ID as the resource key, so we can
1221 // avoid duplications.
1222 const UChar
* newKey
= ZoneMeta::findMetaZoneID(mzID
);
1223 if (newKey
!= NULL
) {
1224 uhash_put(fMZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1225 if (U_FAILURE(status
)) {
1226 if (znames
!= NULL
) {
1230 } else if (znames
!= NULL
) {
1231 // put the name info into the trie
1232 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1233 const UChar
* name
= znames
->getName(ALL_NAME_TYPES
[i
]);
1235 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1236 if (nameinfo
!= NULL
) {
1237 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1238 nameinfo
->tzID
= NULL
;
1239 nameinfo
->mzID
= newKey
;
1240 fNamesTrie
.put(name
, nameinfo
, status
);
1247 // Should never happen with a valid input
1248 if (znames
!= NULL
) {
1249 // It's not possible that we get a valid ZNames with unknown ID.
1250 // But just in case..
1255 } else if (cacheVal
!= EMPTY
) {
1256 znames
= (ZNames
*)cacheVal
;
1263 * This method updates the cache and must be called with a lock
1266 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
) {
1267 if (tzID
.length() > ZID_KEY_MAX
) {
1271 TZNames
*tznames
= NULL
;
1273 UErrorCode status
= U_ZERO_ERROR
;
1274 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1275 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1276 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1277 tzIDKey
[tzIDKeyLen
] = 0;
1279 void *cacheVal
= uhash_get(fTZNamesMap
, tzIDKey
);
1280 if (cacheVal
== NULL
) {
1281 char key
[ZID_KEY_MAX
+ 1];
1282 UErrorCode status
= U_ZERO_ERROR
;
1283 // Replace "/" with ":".
1284 UnicodeString
uKey(tzID
);
1285 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
1286 if (uKey
.charAt(i
) == (UChar
)0x2F) {
1287 uKey
.setCharAt(i
, (UChar
)0x3A);
1290 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
1291 tznames
= TZNames::createInstance(fZoneStrings
, key
, tzID
);
1293 if (tznames
== NULL
) {
1294 cacheVal
= (void *)EMPTY
;
1298 // Use the persistent ID as the resource key, so we can
1299 // avoid duplications.
1300 const UChar
* newKey
= ZoneMeta::findTimeZoneID(tzID
);
1301 if (newKey
!= NULL
) {
1302 uhash_put(fTZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1303 if (U_FAILURE(status
)) {
1304 if (tznames
!= NULL
) {
1308 } else if (tznames
!= NULL
) {
1309 // put the name info into the trie
1310 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1311 const UChar
* name
= tznames
->getName(ALL_NAME_TYPES
[i
]);
1313 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1314 if (nameinfo
!= NULL
) {
1315 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1316 nameinfo
->tzID
= newKey
;
1317 nameinfo
->mzID
= NULL
;
1318 fNamesTrie
.put(name
, nameinfo
, status
);
1324 // Should never happen with a valid input
1325 if (tznames
!= NULL
) {
1326 // It's not possible that we get a valid TZNames with unknown ID.
1327 // But just in case..
1332 } else if (cacheVal
!= EMPTY
) {
1333 tznames
= (TZNames
*)cacheVal
;
1339 TimeZoneNames::MatchInfoCollection
*
1340 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1341 ZNameSearchHandler
handler(types
);
1343 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1347 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1349 umtx_unlock(&gLock
);
1351 if (U_FAILURE(status
)) {
1356 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1357 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1364 // All names are not yet loaded into the trie
1367 if (!fNamesTrieFullyLoaded
) {
1368 const UnicodeString
*id
;
1370 // load strings for all zones
1371 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1372 if (U_SUCCESS(status
)) {
1373 while ((id
= tzIDs
->snext(status
))) {
1374 if (U_FAILURE(status
)) {
1377 // loadStrings also load related metazone strings
1378 nonConstThis
->loadStrings(*id
);
1381 if (tzIDs
!= NULL
) {
1384 if (U_SUCCESS(status
)) {
1385 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1389 umtx_unlock(&gLock
);
1391 if (U_FAILURE(status
)) {
1398 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1400 umtx_unlock(&gLock
);
1402 return handler
.getMatches(maxLen
);
1405 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1406 static const int32_t gEtcPrefixLen
= 4;
1407 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1408 static const int32_t gSystemVPrefixLen
= 8;
1409 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1410 static const int32_t gRiyadh8Len
= 7;
1412 UnicodeString
& U_EXPORT2
1413 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1414 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1415 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1420 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1421 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1422 name
.setTo(tzID
, sep
+ 1);
1423 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1424 UnicodeString((UChar
)0x20 /* space */));
1431 // ---------------------------------------------------
1432 // TZDBTimeZoneNames and its supporting classes
1434 // TZDBTimeZoneNames is an implementation class of
1435 // TimeZoneNames holding the IANA tz database abbreviations.
1436 // ---------------------------------------------------
1438 class TZDBNames
: public UMemory
{
1440 virtual ~TZDBNames();
1442 static TZDBNames
* createInstance(UResourceBundle
* rb
, const char* key
);
1443 const UChar
* getName(UTimeZoneNameType type
) const;
1444 const char** getParseRegions(int32_t& numRegions
) const;
1447 TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
);
1450 const UChar
** fNames
;
1452 int32_t fNumRegions
;
1455 TZDBNames::TZDBNames(const UChar
** names
, char** regions
, int32_t numRegions
)
1458 fNumRegions(numRegions
) {
1461 TZDBNames::~TZDBNames() {
1462 if (fNames
!= NULL
) {
1465 if (fRegions
!= NULL
) {
1466 char **p
= fRegions
;
1467 for (int32_t i
= 0; i
< fNumRegions
; p
++, i
++) {
1470 uprv_free(fRegions
);
1475 TZDBNames::createInstance(UResourceBundle
* rb
, const char* key
) {
1476 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
1480 UErrorCode status
= U_ZERO_ERROR
;
1482 const UChar
**names
= NULL
;
1483 char** regions
= NULL
;
1484 int32_t numRegions
= 0;
1488 UResourceBundle
* rbTable
= NULL
;
1489 rbTable
= ures_getByKey(rb
, key
, rbTable
, &status
);
1490 if (U_FAILURE(status
)) {
1494 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * TZDBNAMES_KEYS_SIZE
);
1495 UBool isEmpty
= TRUE
;
1496 if (names
!= NULL
) {
1497 for (int32_t i
= 0; i
< TZDBNAMES_KEYS_SIZE
; i
++) {
1498 status
= U_ZERO_ERROR
;
1499 const UChar
*value
= ures_getStringByKey(rbTable
, TZDBNAMES_KEYS
[i
], &len
, &status
);
1500 if (U_FAILURE(status
) || len
== 0) {
1510 if (names
!= NULL
) {
1516 UResourceBundle
*regionsRes
= ures_getByKey(rbTable
, "parseRegions", NULL
, &status
);
1517 UBool regionError
= FALSE
;
1518 if (U_SUCCESS(status
)) {
1519 numRegions
= ures_getSize(regionsRes
);
1520 if (numRegions
> 0) {
1521 regions
= (char**)uprv_malloc(sizeof(char*) * numRegions
);
1522 if (regions
!= NULL
) {
1523 char **pRegion
= regions
;
1524 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1529 for (int32_t i
= 0; i
< numRegions
; i
++, pRegion
++) {
1530 status
= U_ZERO_ERROR
;
1531 const UChar
*uregion
= ures_getStringByIndex(regionsRes
, i
, &len
, &status
);
1532 if (U_FAILURE(status
)) {
1536 *pRegion
= (char*)uprv_malloc(sizeof(char) * (len
+ 1));
1537 if (*pRegion
== NULL
) {
1541 u_UCharsToChars(uregion
, *pRegion
, len
);
1542 (*pRegion
)[len
] = 0;
1547 ures_close(regionsRes
);
1548 ures_close(rbTable
);
1551 if (names
!= NULL
) {
1554 if (regions
!= NULL
) {
1556 for (int32_t i
= 0; i
< numRegions
; p
++, i
++) {
1564 return new TZDBNames(names
, regions
, numRegions
);
1568 TZDBNames::getName(UTimeZoneNameType type
) const {
1569 if (fNames
== NULL
) {
1572 const UChar
*name
= NULL
;
1574 case UTZNM_SHORT_STANDARD
:
1577 case UTZNM_SHORT_DAYLIGHT
:
1587 TZDBNames::getParseRegions(int32_t& numRegions
) const {
1588 if (fRegions
== NULL
) {
1591 numRegions
= fNumRegions
;
1593 return (const char**)fRegions
;
1598 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1601 typedef struct TZDBNameInfo
{
1603 UTimeZoneNameType type
;
1604 UBool ambiguousType
;
1605 const char** parseRegions
;
1611 class TZDBNameSearchHandler
: public TextTrieMapSearchResultHandler
{
1613 TZDBNameSearchHandler(uint32_t types
, const char* region
);
1614 virtual ~TZDBNameSearchHandler();
1616 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
1617 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
1621 int32_t fMaxMatchLen
;
1622 TimeZoneNames::MatchInfoCollection
* fResults
;
1623 const char* fRegion
;
1626 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types
, const char* region
)
1627 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
), fRegion(region
) {
1630 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1631 if (fResults
!= NULL
) {
1637 TZDBNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
1638 if (U_FAILURE(status
)) {
1642 TZDBNameInfo
*match
= NULL
;
1643 TZDBNameInfo
*defaultRegionMatch
= NULL
;
1645 if (node
->hasValues()) {
1646 int32_t valuesCount
= node
->countValues();
1647 for (int32_t i
= 0; i
< valuesCount
; i
++) {
1648 TZDBNameInfo
*ninfo
= (TZDBNameInfo
*)node
->getValue(i
);
1649 if (ninfo
== NULL
) {
1652 if ((ninfo
->type
& fTypes
) != 0) {
1653 // Some tz database abbreviations are ambiguous. For example,
1654 // CST means either Central Standard Time or China Standard Time.
1655 // Unlike CLDR time zone display names, this implementation
1656 // does not use unique names. And TimeZoneFormat does not expect
1657 // multiple results returned for the same time zone type.
1658 // For this reason, this implementation resolve one among same
1659 // zone type with a same name at this level.
1660 if (ninfo
->parseRegions
== NULL
) {
1661 // parseRegions == null means this is the default metazone
1662 // mapping for the abbreviation.
1663 if (defaultRegionMatch
== NULL
) {
1664 match
= defaultRegionMatch
= ninfo
;
1667 UBool matchRegion
= FALSE
;
1668 // non-default metazone mapping for an abbreviation
1669 // comes with applicable regions. For example, the default
1670 // metazone mapping for "CST" is America_Central,
1671 // but if region is one of CN/MO/TW, "CST" is parsed
1672 // as metazone China (China Standard Time).
1673 for (int32_t i
= 0; i
< ninfo
->nRegions
; i
++) {
1674 const char *region
= ninfo
->parseRegions
[i
];
1675 if (uprv_strcmp(fRegion
, region
) == 0) {
1684 if (match
== NULL
) {
1691 if (match
!= NULL
) {
1692 UTimeZoneNameType ntype
= match
->type
;
1693 // Note: Workaround for duplicated standard/daylight names
1694 // The tz database contains a few zones sharing a
1695 // same name for both standard time and daylight saving
1696 // time. For example, Australia/Sydney observes DST,
1697 // but "EST" is used for both standard and daylight.
1698 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1699 // in the find operation, we cannot tell which one was
1700 // actually matched.
1701 // TimeZoneFormat#parse returns a matched name type (standard
1702 // or daylight) and DateFormat implementation uses the info to
1703 // to adjust actual time. To avoid false type information,
1704 // this implementation replaces the name type with SHORT_GENERIC.
1705 if (match
->ambiguousType
1706 && (ntype
== UTZNM_SHORT_STANDARD
|| ntype
== UTZNM_SHORT_DAYLIGHT
)
1707 && (fTypes
& UTZNM_SHORT_STANDARD
) != 0
1708 && (fTypes
& UTZNM_SHORT_DAYLIGHT
) != 0) {
1709 ntype
= UTZNM_SHORT_GENERIC
;
1712 if (fResults
== NULL
) {
1713 fResults
= new TimeZoneNames::MatchInfoCollection();
1714 if (fResults
== NULL
) {
1715 status
= U_MEMORY_ALLOCATION_ERROR
;
1718 if (U_SUCCESS(status
)) {
1719 U_ASSERT(fResults
!= NULL
);
1720 U_ASSERT(match
->mzID
!= NULL
);
1721 fResults
->addMetaZone(ntype
, matchLength
, UnicodeString(match
->mzID
, -1), status
);
1722 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
1723 fMaxMatchLen
= matchLength
;
1731 TimeZoneNames::MatchInfoCollection
*
1732 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
1733 // give the ownership to the caller
1734 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
1735 maxMatchLen
= fMaxMatchLen
;
1745 * Deleter for TZDBNames
1747 static void U_CALLCONV
1748 deleteTZDBNames(void *obj
) {
1750 delete (TZDBNames
*)obj
;
1754 static void U_CALLCONV
initTZDBNamesMap(UErrorCode
&status
) {
1755 gTZDBNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
1756 if (U_FAILURE(status
)) {
1757 gTZDBNamesMap
= NULL
;
1760 // no key deleters for tzdb name maps
1761 uhash_setValueDeleter(gTZDBNamesMap
, deleteTZDBNames
);
1762 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
1766 * Deleter for TZDBNameInfo
1768 static void U_CALLCONV
1769 deleteTZDBNameInfo(void *obj
) {
1775 static void U_CALLCONV
prepareFind(UErrorCode
&status
) {
1776 if (U_FAILURE(status
)) {
1779 gTZDBNamesTrie
= new TextTrieMap(TRUE
, deleteTZDBNameInfo
);
1780 if (gTZDBNamesTrie
== NULL
) {
1781 status
= U_MEMORY_ALLOCATION_ERROR
;
1785 const UnicodeString
*mzID
;
1786 StringEnumeration
*mzIDs
= TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1787 if (U_SUCCESS(status
)) {
1788 while ((mzID
= mzIDs
->snext(status
)) && U_SUCCESS(status
)) {
1789 const TZDBNames
*names
= TZDBTimeZoneNames::getMetaZoneNames(*mzID
, status
);
1790 if (names
== NULL
) {
1793 const UChar
*std
= names
->getName(UTZNM_SHORT_STANDARD
);
1794 const UChar
*dst
= names
->getName(UTZNM_SHORT_DAYLIGHT
);
1795 if (std
== NULL
&& dst
== NULL
) {
1798 int32_t numRegions
= 0;
1799 const char **parseRegions
= names
->getParseRegions(numRegions
);
1801 // The tz database contains a few zones sharing a
1802 // same name for both standard time and daylight saving
1803 // time. For example, Australia/Sydney observes DST,
1804 // but "EST" is used for both standard and daylight.
1805 // we need to store the information for later processing.
1806 UBool ambiguousType
= (std
!= NULL
&& dst
!= NULL
&& u_strcmp(std
, dst
) == 0);
1808 const UChar
*uMzID
= ZoneMeta::findMetaZoneID(*mzID
);
1810 TZDBNameInfo
*stdInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
1811 if (stdInf
== NULL
) {
1812 status
= U_MEMORY_ALLOCATION_ERROR
;
1815 stdInf
->mzID
= uMzID
;
1816 stdInf
->type
= UTZNM_SHORT_STANDARD
;
1817 stdInf
->ambiguousType
= ambiguousType
;
1818 stdInf
->parseRegions
= parseRegions
;
1819 stdInf
->nRegions
= numRegions
;
1820 gTZDBNamesTrie
->put(std
, stdInf
, status
);
1822 if (U_SUCCESS(status
) && dst
!= NULL
) {
1823 TZDBNameInfo
*dstInf
= (TZDBNameInfo
*)uprv_malloc(sizeof(TZDBNameInfo
));
1824 if (dstInf
== NULL
) {
1825 status
= U_MEMORY_ALLOCATION_ERROR
;
1828 dstInf
->mzID
= uMzID
;
1829 dstInf
->type
= UTZNM_SHORT_DAYLIGHT
;
1830 dstInf
->ambiguousType
= ambiguousType
;
1831 dstInf
->parseRegions
= parseRegions
;
1832 dstInf
->nRegions
= numRegions
;
1833 gTZDBNamesTrie
->put(dst
, dstInf
, status
);
1839 if (U_FAILURE(status
)) {
1840 delete gTZDBNamesTrie
;
1841 gTZDBNamesTrie
= NULL
;
1845 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES
, tzdbTimeZoneNames_cleanup
);
1850 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale
& locale
)
1852 UBool useWorld
= TRUE
;
1853 const char* region
= fLocale
.getCountry();
1854 int32_t regionLen
= uprv_strlen(region
);
1855 if (regionLen
== 0) {
1856 UErrorCode status
= U_ZERO_ERROR
;
1857 char loc
[ULOC_FULLNAME_CAPACITY
];
1858 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
1859 regionLen
= uloc_getCountry(loc
, fRegion
, sizeof(fRegion
), &status
);
1860 if (U_SUCCESS(status
) && regionLen
< (int32_t)sizeof(fRegion
)) {
1863 } else if (regionLen
< (int32_t)sizeof(fRegion
)) {
1864 uprv_strcpy(fRegion
, region
);
1868 uprv_strcpy(fRegion
, "001");
1872 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
1876 TZDBTimeZoneNames::operator==(const TimeZoneNames
& other
) const {
1877 if (this == &other
) {
1880 // No implementation for now
1885 TZDBTimeZoneNames::clone() const {
1886 return new TZDBTimeZoneNames(fLocale
);
1890 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
1891 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status
);
1895 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1896 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID
, status
);
1900 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1901 return TimeZoneNamesImpl::_getMetaZoneID(tzID
, date
, mzID
);
1905 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1906 return TimeZoneNamesImpl::_getReferenceZoneID(mzID
, region
, tzID
);
1910 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1911 UTimeZoneNameType type
,
1912 UnicodeString
& name
) const {
1914 if (mzID
.isEmpty()) {
1918 UErrorCode status
= U_ZERO_ERROR
;
1919 const TZDBNames
*tzdbNames
= TZDBTimeZoneNames::getMetaZoneNames(mzID
, status
);
1920 if (U_SUCCESS(status
)) {
1921 const UChar
*s
= tzdbNames
->getName(type
);
1923 name
.setTo(TRUE
, s
, -1);
1931 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString
& /* tzID */, UTimeZoneNameType
/* type */, UnicodeString
& name
) const {
1932 // No abbreviations associated a zone directly for now.
1937 TZDBTimeZoneNames::MatchInfoCollection
*
1938 TZDBTimeZoneNames::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1939 umtx_initOnce(gTZDBNamesTrieInitOnce
, &prepareFind
, status
);
1940 if (U_FAILURE(status
)) {
1944 TZDBNameSearchHandler
handler(types
, fRegion
);
1945 gTZDBNamesTrie
->search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1946 if (U_FAILURE(status
)) {
1950 return handler
.getMatches(maxLen
);
1954 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString
& mzID
, UErrorCode
& status
) {
1955 umtx_initOnce(gTZDBNamesMapInitOnce
, &initTZDBNamesMap
, status
);
1956 if (U_FAILURE(status
)) {
1960 TZDBNames
* tzdbNames
= NULL
;
1962 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1963 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1964 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1965 mzIDKey
[mzID
.length()] = 0;
1967 umtx_lock(&gTZDBNamesMapLock
);
1969 void *cacheVal
= uhash_get(gTZDBNamesMap
, mzIDKey
);
1970 if (cacheVal
== NULL
) {
1971 UResourceBundle
*zoneStringsRes
= ures_openDirect(U_ICUDATA_ZONE
, "tzdbNames", &status
);
1972 zoneStringsRes
= ures_getByKey(zoneStringsRes
, gZoneStrings
, zoneStringsRes
, &status
);
1973 if (U_SUCCESS(status
)) {
1974 char key
[ZID_KEY_MAX
+ 1];
1975 mergeTimeZoneKey(mzID
, key
);
1976 tzdbNames
= TZDBNames::createInstance(zoneStringsRes
, key
);
1978 if (tzdbNames
== NULL
) {
1979 cacheVal
= (void *)EMPTY
;
1981 cacheVal
= tzdbNames
;
1983 // Use the persistent ID as the resource key, so we can
1984 // avoid duplications.
1985 const UChar
* newKey
= ZoneMeta::findMetaZoneID(mzID
);
1986 if (newKey
!= NULL
) {
1987 uhash_put(gTZDBNamesMap
, (void *)newKey
, cacheVal
, &status
);
1988 if (U_FAILURE(status
)) {
1989 if (tzdbNames
!= NULL
) {
1995 // Should never happen with a valid input
1996 if (tzdbNames
!= NULL
) {
1997 // It's not possible that we get a valid tzdbNames with unknown ID.
1998 // But just in case..
2004 ures_close(zoneStringsRes
);
2005 } else if (cacheVal
!= EMPTY
) {
2006 tzdbNames
= (TZDBNames
*)cacheVal
;
2009 umtx_unlock(&gTZDBNamesMapLock
);
2017 #endif /* #if !UCONFIG_NO_FORMATTING */