2 *******************************************************************************
3 * Copyright (C) 2011-2013, 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
= (sizeof KEYS
/ sizeof KEYS
[0]);
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 #define DEFAULT_CHARACTERNODE_CAPACITY 1
56 // ---------------------------------------------------
57 // CharacterNode class implementation
58 // ---------------------------------------------------
59 void CharacterNode::clear() {
60 uprv_memset(this, 0, sizeof(*this));
63 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
64 if (fValues
== NULL
) {
66 } else if (!fHasValuesVector
) {
68 valueDeleter(fValues
);
71 delete (UVector
*)fValues
;
76 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
77 if (U_FAILURE(status
)) {
83 if (fValues
== NULL
) {
86 // At least one value already.
87 if (!fHasValuesVector
) {
88 // There is only one value so far, and not in a vector yet.
89 // Create a vector and add the old value.
90 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
91 if (U_FAILURE(status
)) {
97 values
->addElement(fValues
, status
);
99 fHasValuesVector
= TRUE
;
101 // Add the new value.
102 ((UVector
*)fValues
)->addElement(value
, status
);
106 // ---------------------------------------------------
107 // TextTrieMapSearchResultHandler class implementation
108 // ---------------------------------------------------
109 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
112 // ---------------------------------------------------
113 // TextTrieMap class implementation
114 // ---------------------------------------------------
115 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
116 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
117 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
120 TextTrieMap::~TextTrieMap() {
122 for (index
= 0; index
< fNodesCount
; ++index
) {
123 fNodes
[index
].deleteValues(fValueDeleter
);
126 if (fLazyContents
!= NULL
) {
127 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
129 fValueDeleter(fLazyContents
->elementAt(i
+1));
132 delete fLazyContents
;
136 int32_t TextTrieMap::isEmpty() const {
137 // Use a separate field for fIsEmpty because it will remain unchanged once the
138 // Trie is built, while fNodes and fLazyContents change with the lazy init
139 // of the nodes structure. Trying to test the changing fields has
140 // thread safety complications.
145 // We defer actually building the TextTrieMap node structure until the first time a
146 // search is performed. put() simply saves the parameters in case we do
147 // eventually need to build it.
150 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
151 const UChar
*s
= sp
.get(key
, status
);
152 put(s
, value
, status
);
155 // This method is for designed for a persistent key, such as string key stored in
158 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
160 if (fLazyContents
== NULL
) {
161 fLazyContents
= new UVector(status
);
162 if (fLazyContents
== NULL
) {
163 status
= U_MEMORY_ALLOCATION_ERROR
;
166 if (U_FAILURE(status
)) {
169 U_ASSERT(fLazyContents
!= NULL
);
170 UChar
*s
= const_cast<UChar
*>(key
);
171 fLazyContents
->addElement(s
, status
);
172 fLazyContents
->addElement(value
, status
);
176 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
177 if (fNodes
== NULL
) {
178 fNodesCapacity
= 512;
179 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
180 fNodes
[0].clear(); // Init root node.
184 UnicodeString foldedKey
;
185 const UChar
*keyBuffer
;
188 // Ok to use fastCopyFrom() because we discard the copy when we return.
189 foldedKey
.fastCopyFrom(key
).foldCase();
190 keyBuffer
= foldedKey
.getBuffer();
191 keyLength
= foldedKey
.length();
193 keyBuffer
= key
.getBuffer();
194 keyLength
= key
.length();
197 CharacterNode
*node
= fNodes
;
199 for (index
= 0; index
< keyLength
; ++index
) {
200 node
= addChildNode(node
, keyBuffer
[index
], status
);
202 node
->addValue(value
, fValueDeleter
, status
);
206 TextTrieMap::growNodes() {
207 if (fNodesCapacity
== 0xffff) {
208 return FALSE
; // We use 16-bit node indexes.
210 int32_t newCapacity
= fNodesCapacity
+ 1000;
211 if (newCapacity
> 0xffff) {
212 newCapacity
= 0xffff;
214 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
215 if (newNodes
== NULL
) {
218 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
221 fNodesCapacity
= newCapacity
;
226 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
227 if (U_FAILURE(status
)) {
230 // Linear search of the sorted list of children.
231 uint16_t prevIndex
= 0;
232 uint16_t nodeIndex
= parent
->fFirstChild
;
233 while (nodeIndex
> 0) {
234 CharacterNode
*current
= fNodes
+ nodeIndex
;
235 UChar childCharacter
= current
->fCharacter
;
236 if (childCharacter
== c
) {
238 } else if (childCharacter
> c
) {
241 prevIndex
= nodeIndex
;
242 nodeIndex
= current
->fNextSibling
;
245 // Ensure capacity. Grow fNodes[] if needed.
246 if (fNodesCount
== fNodesCapacity
) {
247 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
249 status
= U_MEMORY_ALLOCATION_ERROR
;
252 parent
= fNodes
+ parentIndex
;
255 // Insert a new child node with c in sorted order.
256 CharacterNode
*node
= fNodes
+ fNodesCount
;
258 node
->fCharacter
= c
;
259 node
->fNextSibling
= nodeIndex
;
260 if (prevIndex
== 0) {
261 parent
->fFirstChild
= (uint16_t)fNodesCount
;
263 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
270 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
271 // Linear search of the sorted list of children.
272 uint16_t nodeIndex
= parent
->fFirstChild
;
273 while (nodeIndex
> 0) {
274 CharacterNode
*current
= fNodes
+ nodeIndex
;
275 UChar childCharacter
= current
->fCharacter
;
276 if (childCharacter
== c
) {
278 } else if (childCharacter
> c
) {
281 nodeIndex
= current
->fNextSibling
;
286 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
287 static UMutex TextTrieMutex
= U_MUTEX_INITIALIZER
;
289 // buildTrie() - The Trie node structure is needed. Create it from the data that was
290 // saved at the time the ZoneStringFormatter was created. The Trie is only
291 // needed for parsing operations, which are less common than formatting,
292 // and the Trie is big, which is why its creation is deferred until first use.
293 void TextTrieMap::buildTrie(UErrorCode
&status
) {
294 if (fLazyContents
!= NULL
) {
295 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
296 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
297 void *val
= fLazyContents
->elementAt(i
+1);
298 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
299 putImpl(keyString
, val
, status
);
301 delete fLazyContents
;
302 fLazyContents
= NULL
;
307 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
308 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
310 // TODO: if locking the mutex for each check proves to be a performance problem,
311 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
312 // the ICU atomic safe functions for assigning and testing.
313 // Don't test the pointer fLazyContents.
314 // Don't do unless it's really required.
315 Mutex
lock(&TextTrieMutex
);
316 if (fLazyContents
!= NULL
) {
317 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
318 nonConstThis
->buildTrie(status
);
321 if (fNodes
== NULL
) {
324 search(fNodes
, text
, start
, start
, handler
, status
);
328 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
329 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
330 if (U_FAILURE(status
)) {
333 if (node
->hasValues()) {
334 if (!handler
->handleMatch(index
- start
, node
, status
)) {
337 if (U_FAILURE(status
)) {
341 UChar32 c
= text
.char32At(index
);
343 // size of character may grow after fold operation
344 UnicodeString
tmp(c
);
347 while (tmpidx
< tmp
.length()) {
348 c
= tmp
.char32At(tmpidx
);
349 node
= getChildNode(node
, c
);
353 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
356 node
= getChildNode(node
, c
);
359 search(node
, text
, start
, index
+1, handler
, status
);
363 // ---------------------------------------------------
364 // ZNStringPool class implementation
365 // ---------------------------------------------------
366 static const int32_t POOL_CHUNK_SIZE
= 2000;
367 struct ZNStringPoolChunk
: public UMemory
{
368 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
369 int32_t fLimit
; // Index to start of unused area at end of fStrings
370 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
374 ZNStringPoolChunk::ZNStringPoolChunk() {
379 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
382 if (U_FAILURE(status
)) {
385 fChunks
= new ZNStringPoolChunk
;
386 if (fChunks
== NULL
) {
387 status
= U_MEMORY_ALLOCATION_ERROR
;
391 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
392 uhash_compareUChars
/* keyComp */,
393 uhash_compareUChars
/* valueComp */,
395 if (U_FAILURE(status
)) {
400 ZNStringPool::~ZNStringPool() {
406 while (fChunks
!= NULL
) {
407 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
413 static const UChar EmptyString
= 0;
415 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
416 const UChar
*pooledString
;
417 if (U_FAILURE(status
)) {
421 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
422 if (pooledString
!= NULL
) {
426 int32_t length
= u_strlen(s
);
427 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
428 if (remainingLength
<= length
) {
429 U_ASSERT(length
< POOL_CHUNK_SIZE
);
430 if (length
>= POOL_CHUNK_SIZE
) {
431 status
= U_INTERNAL_PROGRAM_ERROR
;
434 ZNStringPoolChunk
*oldChunk
= fChunks
;
435 fChunks
= new ZNStringPoolChunk
;
436 if (fChunks
== NULL
) {
437 status
= U_MEMORY_ALLOCATION_ERROR
;
440 fChunks
->fNext
= oldChunk
;
443 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
444 u_strcpy(destString
, s
);
445 fChunks
->fLimit
+= (length
+ 1);
446 uhash_put(fHash
, destString
, destString
, &status
);
452 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
453 // into the pool's storage. Used for strings from resource bundles,
454 // which will perisist for the life of the zone string formatter, and
455 // therefore can be used directly without copying.
456 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
457 const UChar
*pooledString
;
458 if (U_FAILURE(status
)) {
462 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
463 if (pooledString
== NULL
) {
464 UChar
*ncs
= const_cast<UChar
*>(s
);
465 uhash_put(fHash
, ncs
, ncs
, &status
);
472 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
473 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
474 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
478 * freeze(). Close the hash table that maps to the pooled strings.
479 * After freezing, the pool can not be searched or added to,
480 * but all existing references to pooled strings remain valid.
482 * The main purpose is to recover the storage used for the hash.
484 void ZNStringPool::freeze() {
490 // ---------------------------------------------------
491 // ZNames - names common for time zone and meta zone
492 // ---------------------------------------------------
493 class ZNames
: public UMemory
{
497 static ZNames
* createInstance(UResourceBundle
* rb
, const char* key
);
498 virtual const UChar
* getName(UTimeZoneNameType type
);
501 ZNames(const UChar
** names
);
502 static const UChar
** loadData(UResourceBundle
* rb
, const char* key
);
505 const UChar
** fNames
;
508 ZNames::ZNames(const UChar
** names
)
513 if (fNames
!= NULL
) {
519 ZNames::createInstance(UResourceBundle
* rb
, const char* key
) {
520 const UChar
** names
= loadData(rb
, key
);
522 // No names data available
525 return new ZNames(names
);
529 ZNames::getName(UTimeZoneNameType type
) {
530 if (fNames
== NULL
) {
533 const UChar
*name
= NULL
;
535 case UTZNM_LONG_GENERIC
:
538 case UTZNM_LONG_STANDARD
:
541 case UTZNM_LONG_DAYLIGHT
:
544 case UTZNM_SHORT_GENERIC
:
547 case UTZNM_SHORT_STANDARD
:
550 case UTZNM_SHORT_DAYLIGHT
:
553 case UTZNM_EXEMPLAR_LOCATION
: // implemeted by subclass
561 ZNames::loadData(UResourceBundle
* rb
, const char* key
) {
562 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
566 UErrorCode status
= U_ZERO_ERROR
;
567 const UChar
**names
= NULL
;
569 UResourceBundle
* rbTable
= NULL
;
570 rbTable
= ures_getByKeyWithFallback(rb
, key
, rbTable
, &status
);
571 if (U_SUCCESS(status
)) {
572 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * KEYS_SIZE
);
574 UBool isEmpty
= TRUE
;
575 for (int32_t i
= 0; i
< KEYS_SIZE
; i
++) {
576 status
= U_ZERO_ERROR
;
578 const UChar
*value
= ures_getStringByKeyWithFallback(rbTable
, KEYS
[i
], &len
, &status
);
579 if (U_FAILURE(status
) || len
== 0) {
587 // No need to keep the names array
597 // ---------------------------------------------------
598 // TZNames - names for a time zone
599 // ---------------------------------------------------
600 class TZNames
: public ZNames
{
604 static TZNames
* createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
);
605 virtual const UChar
* getName(UTimeZoneNameType type
);
608 TZNames(const UChar
** names
);
609 const UChar
* fLocationName
;
610 UChar
* fLocationNameOwned
;
613 TZNames::TZNames(const UChar
** names
)
614 : ZNames(names
), fLocationName(NULL
), fLocationNameOwned(NULL
) {
617 TZNames::~TZNames() {
618 if (fLocationNameOwned
) {
619 uprv_free(fLocationNameOwned
);
624 TZNames::getName(UTimeZoneNameType type
) {
625 if (type
== UTZNM_EXEMPLAR_LOCATION
) {
626 return fLocationName
;
628 return ZNames::getName(type
);
632 TZNames::createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
) {
633 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
637 const UChar
** names
= loadData(rb
, key
);
638 const UChar
* locationName
= NULL
;
639 UChar
* locationNameOwned
= NULL
;
641 UErrorCode status
= U_ZERO_ERROR
;
644 UResourceBundle
* table
= ures_getByKeyWithFallback(rb
, key
, NULL
, &status
);
645 locationName
= ures_getStringByKeyWithFallback(table
, gEcTag
, &len
, &status
);
646 // ignore missing resource here
647 status
= U_ZERO_ERROR
;
651 if (locationName
== NULL
) {
652 UnicodeString tmpName
;
653 int32_t tmpNameLen
= 0;
654 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, tmpName
);
655 tmpNameLen
= tmpName
.length();
657 if (tmpNameLen
> 0) {
658 locationNameOwned
= (UChar
*) uprv_malloc(sizeof(UChar
) * (tmpNameLen
+ 1));
659 if (locationNameOwned
) {
660 tmpName
.extract(locationNameOwned
, tmpNameLen
+ 1, status
);
661 locationName
= locationNameOwned
;
666 TZNames
* tznames
= NULL
;
667 if (locationName
!= NULL
|| names
!= NULL
) {
668 tznames
= new TZNames(names
);
669 if (tznames
== NULL
) {
670 if (locationNameOwned
) {
671 uprv_free(locationNameOwned
);
674 tznames
->fLocationName
= locationName
;
675 tznames
->fLocationNameOwned
= locationNameOwned
;
681 // ---------------------------------------------------
682 // The meta zone ID enumeration class
683 // ---------------------------------------------------
684 class MetaZoneIDsEnumeration
: public StringEnumeration
{
686 MetaZoneIDsEnumeration();
687 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
688 MetaZoneIDsEnumeration(UVector
* mzIDs
);
689 virtual ~MetaZoneIDsEnumeration();
690 static UClassID U_EXPORT2
getStaticClassID(void);
691 virtual UClassID
getDynamicClassID(void) const;
692 virtual const UnicodeString
* snext(UErrorCode
& status
);
693 virtual void reset(UErrorCode
& status
);
694 virtual int32_t count(UErrorCode
& status
) const;
698 const UVector
* fMetaZoneIDs
;
699 UVector
*fLocalVector
;
702 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
704 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
705 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
708 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
709 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
710 fLen
= fMetaZoneIDs
->size();
713 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
714 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
716 fLen
= fMetaZoneIDs
->size();
721 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
722 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
723 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
730 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
735 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
739 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
747 * ZNameInfo stores zone name information in the trie
749 typedef struct ZNameInfo
{
750 UTimeZoneNameType type
;
756 * ZMatchInfo stores zone name match information used by find method
758 typedef struct ZMatchInfo
{
759 const ZNameInfo
* znameInfo
;
765 // ---------------------------------------------------
766 // ZNameSearchHandler
767 // ---------------------------------------------------
768 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
770 ZNameSearchHandler(uint32_t types
);
771 virtual ~ZNameSearchHandler();
773 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
774 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
778 int32_t fMaxMatchLen
;
779 TimeZoneNames::MatchInfoCollection
* fResults
;
782 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
783 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
786 ZNameSearchHandler::~ZNameSearchHandler() {
787 if (fResults
!= NULL
) {
793 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
794 if (U_FAILURE(status
)) {
797 if (node
->hasValues()) {
798 int32_t valuesCount
= node
->countValues();
799 for (int32_t i
= 0; i
< valuesCount
; i
++) {
800 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
801 if (nameinfo
== NULL
) {
804 if ((nameinfo
->type
& fTypes
) != 0) {
805 // matches a requested type
806 if (fResults
== NULL
) {
807 fResults
= new TimeZoneNames::MatchInfoCollection();
808 if (fResults
== NULL
) {
809 status
= U_MEMORY_ALLOCATION_ERROR
;
812 if (U_SUCCESS(status
)) {
813 U_ASSERT(fResults
!= NULL
);
814 if (nameinfo
->tzID
) {
815 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
817 U_ASSERT(nameinfo
->mzID
);
818 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
820 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
821 fMaxMatchLen
= matchLength
;
830 TimeZoneNames::MatchInfoCollection
*
831 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
832 // give the ownership to the caller
833 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
834 maxMatchLen
= fMaxMatchLen
;
842 // ---------------------------------------------------
845 // TimeZoneNames implementation class. This is the main
846 // part of this module.
847 // ---------------------------------------------------
853 static void U_CALLCONV
854 deleteZNames(void *obj
) {
856 delete (ZNames
*)obj
;
860 * Deleter for TZNames
862 static void U_CALLCONV
863 deleteTZNames(void *obj
) {
865 delete (TZNames
*)obj
;
870 * Deleter for ZNameInfo
872 static void U_CALLCONV
873 deleteZNameInfo(void *obj
) {
879 static UMutex gLock
= U_MUTEX_INITIALIZER
;
881 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
886 fNamesTrieFullyLoaded(FALSE
),
887 fNamesTrie(TRUE
, deleteZNameInfo
) {
888 initialize(locale
, status
);
892 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
893 if (U_FAILURE(status
)) {
897 // Load zoneStrings bundle
898 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
899 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
900 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
901 if (U_FAILURE(tmpsts
)) {
907 // Initialize hashtables holding time zone/meta zone names
908 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
909 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
910 if (U_FAILURE(status
)) {
915 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
916 uhash_setValueDeleter(fTZNamesMap
, deleteTZNames
);
917 // no key deleters for name maps
919 // preload zone strings for the default zone
920 TimeZone
*tz
= TimeZone::createDefault();
921 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
923 loadStrings(UnicodeString(tzID
));
931 * This method updates the cache and must be called with a lock,
932 * except initializer.
935 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
) {
936 loadTimeZoneNames(tzCanonicalID
);
938 UErrorCode status
= U_ZERO_ERROR
;
939 StringEnumeration
*mzIDs
= getAvailableMetaZoneIDs(tzCanonicalID
, status
);
940 if (U_SUCCESS(status
) && mzIDs
!= NULL
) {
941 const UnicodeString
*mzID
;
942 while ((mzID
= mzIDs
->snext(status
))) {
943 if (U_FAILURE(status
)) {
946 loadMetaZoneNames(*mzID
);
952 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
957 TimeZoneNamesImpl::cleanup() {
958 if (fZoneStrings
!= NULL
) {
959 ures_close(fZoneStrings
);
962 if (fMZNamesMap
!= NULL
) {
963 uhash_close(fMZNamesMap
);
966 if (fTZNamesMap
!= NULL
) {
967 uhash_close(fTZNamesMap
);
973 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
974 if (this == &other
) {
977 // No implementation for now
982 TimeZoneNamesImpl::clone() const {
983 UErrorCode status
= U_ZERO_ERROR
;
984 return new TimeZoneNamesImpl(fLocale
, status
);
988 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
989 if (U_FAILURE(status
)) {
992 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
994 return new MetaZoneIDsEnumeration();
996 return new MetaZoneIDsEnumeration(*mzIDs
);
1000 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
1001 if (U_FAILURE(status
)) {
1004 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1005 if (mappings
== NULL
) {
1006 return new MetaZoneIDsEnumeration();
1009 MetaZoneIDsEnumeration
*senum
= NULL
;
1010 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1011 if (mzIDs
== NULL
) {
1012 status
= U_MEMORY_ALLOCATION_ERROR
;
1014 if (U_SUCCESS(status
)) {
1015 U_ASSERT(mzIDs
!= NULL
);
1016 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1018 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1019 const UChar
*mzID
= map
->mzid
;
1020 if (!mzIDs
->contains((void *)mzID
)) {
1021 mzIDs
->addElement((void *)mzID
, status
);
1024 if (U_SUCCESS(status
)) {
1025 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1034 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1035 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1040 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1041 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1046 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1047 UTimeZoneNameType type
,
1048 UnicodeString
& name
) const {
1049 name
.setToBogus(); // cleanup result.
1050 if (mzID
.isEmpty()) {
1054 ZNames
*znames
= NULL
;
1055 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1059 znames
= nonConstThis
->loadMetaZoneNames(mzID
);
1061 umtx_unlock(&gLock
);
1063 if (znames
!= NULL
) {
1064 const UChar
* s
= znames
->getName(type
);
1066 name
.setTo(TRUE
, s
, -1);
1073 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1074 name
.setToBogus(); // cleanup result.
1075 if (tzID
.isEmpty()) {
1079 TZNames
*tznames
= NULL
;
1080 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1084 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1086 umtx_unlock(&gLock
);
1088 if (tznames
!= NULL
) {
1089 const UChar
*s
= tznames
->getName(type
);
1091 name
.setTo(TRUE
, s
, -1);
1098 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1099 name
.setToBogus(); // cleanup result.
1100 const UChar
* locName
= NULL
;
1101 TZNames
*tznames
= NULL
;
1102 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1106 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1108 umtx_unlock(&gLock
);
1110 if (tznames
!= NULL
) {
1111 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1113 if (locName
!= NULL
) {
1114 name
.setTo(TRUE
, locName
, -1);
1121 // Merge the MZ_PREFIX and mzId
1122 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1123 if (mzID
.isEmpty()) {
1128 char mzIdChar
[ZID_KEY_MAX
+ 1];
1130 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1131 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1132 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1133 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1134 result
[keyLen
+ prefixLen
] = '\0';
1138 * This method updates the cache and must be called with a lock
1141 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
) {
1142 if (mzID
.length() > (ZID_KEY_MAX
- MZ_PREFIX_LEN
)) {
1146 ZNames
*znames
= NULL
;
1148 UErrorCode status
= U_ZERO_ERROR
;
1149 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1150 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1151 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1152 mzIDKey
[mzID
.length()] = 0;
1154 void *cacheVal
= uhash_get(fMZNamesMap
, mzIDKey
);
1155 if (cacheVal
== NULL
) {
1156 char key
[ZID_KEY_MAX
+ 1];
1157 mergeTimeZoneKey(mzID
, key
);
1158 znames
= ZNames::createInstance(fZoneStrings
, key
);
1160 if (znames
== NULL
) {
1161 cacheVal
= (void *)EMPTY
;
1165 // Use the persistent ID as the resource key, so we can
1166 // avoid duplications.
1167 const UChar
* newKey
= ZoneMeta::findMetaZoneID(mzID
);
1168 if (newKey
!= NULL
) {
1169 uhash_put(fMZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1170 if (U_FAILURE(status
)) {
1171 if (znames
!= NULL
) {
1174 } else if (znames
!= NULL
) {
1175 // put the name info into the trie
1176 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1177 const UChar
* name
= znames
->getName(ALL_NAME_TYPES
[i
]);
1179 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1180 if (nameinfo
!= NULL
) {
1181 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1182 nameinfo
->tzID
= NULL
;
1183 nameinfo
->mzID
= newKey
;
1184 fNamesTrie
.put(name
, nameinfo
, status
);
1191 // Should never happen with a valid input
1192 if (znames
!= NULL
) {
1193 // It's not possible that we get a valid ZNames with unknown ID.
1194 // But just in case..
1199 } else if (cacheVal
!= EMPTY
) {
1200 znames
= (ZNames
*)cacheVal
;
1207 * This method updates the cache and must be called with a lock
1210 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
) {
1211 if (tzID
.length() > ZID_KEY_MAX
) {
1215 TZNames
*tznames
= NULL
;
1217 UErrorCode status
= U_ZERO_ERROR
;
1218 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1219 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1220 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1221 tzIDKey
[tzIDKeyLen
] = 0;
1223 void *cacheVal
= uhash_get(fTZNamesMap
, tzIDKey
);
1224 if (cacheVal
== NULL
) {
1225 char key
[ZID_KEY_MAX
+ 1];
1226 UErrorCode status
= U_ZERO_ERROR
;
1227 // Replace "/" with ":".
1228 UnicodeString
uKey(tzID
);
1229 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
1230 if (uKey
.charAt(i
) == (UChar
)0x2F) {
1231 uKey
.setCharAt(i
, (UChar
)0x3A);
1234 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
1235 tznames
= TZNames::createInstance(fZoneStrings
, key
, tzID
);
1237 if (tznames
== NULL
) {
1238 cacheVal
= (void *)EMPTY
;
1242 // Use the persistent ID as the resource key, so we can
1243 // avoid duplications.
1244 const UChar
* newKey
= ZoneMeta::findTimeZoneID(tzID
);
1245 if (newKey
!= NULL
) {
1246 uhash_put(fTZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1247 if (U_FAILURE(status
)) {
1248 if (tznames
!= NULL
) {
1251 } else if (tznames
!= NULL
) {
1252 // put the name info into the trie
1253 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1254 const UChar
* name
= tznames
->getName(ALL_NAME_TYPES
[i
]);
1256 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1257 if (nameinfo
!= NULL
) {
1258 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1259 nameinfo
->tzID
= newKey
;
1260 nameinfo
->mzID
= NULL
;
1261 fNamesTrie
.put(name
, nameinfo
, status
);
1267 // Should never happen with a valid input
1268 if (tznames
!= NULL
) {
1269 // It's not possible that we get a valid TZNames with unknown ID.
1270 // But just in case..
1275 } else if (cacheVal
!= EMPTY
) {
1276 tznames
= (TZNames
*)cacheVal
;
1282 TimeZoneNames::MatchInfoCollection
*
1283 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1284 ZNameSearchHandler
handler(types
);
1286 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1290 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1292 umtx_unlock(&gLock
);
1294 if (U_FAILURE(status
)) {
1299 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1300 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1307 // All names are not yet loaded into the trie
1310 if (!fNamesTrieFullyLoaded
) {
1311 const UnicodeString
*id
;
1313 // load strings for all zones
1314 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1315 if (U_SUCCESS(status
)) {
1316 while ((id
= tzIDs
->snext(status
))) {
1317 if (U_FAILURE(status
)) {
1320 // loadStrings also load related metazone strings
1321 nonConstThis
->loadStrings(*id
);
1324 if (tzIDs
!= NULL
) {
1327 if (U_SUCCESS(status
)) {
1328 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1332 umtx_unlock(&gLock
);
1334 if (U_FAILURE(status
)) {
1341 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1343 umtx_unlock(&gLock
);
1345 return handler
.getMatches(maxLen
);
1348 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1349 static const int32_t gEtcPrefixLen
= 4;
1350 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1351 static const int32_t gSystemVPrefixLen
= 8;
1352 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1353 static const int32_t gRiyadh8Len
= 7;
1355 UnicodeString
& U_EXPORT2
1356 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1357 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1358 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1363 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1364 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1365 name
.setTo(tzID
, sep
+ 1);
1366 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1367 UnicodeString((UChar
)0x20 /* space */));
1377 #endif /* #if !UCONFIG_NO_FORMATTING */