2 *******************************************************************************
3 * Copyright (C) 2011-2012, 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"
33 #define ZID_KEY_MAX 128
34 #define MZ_PREFIX_LEN 5
36 static const char gZoneStrings
[] = "zoneStrings";
37 static const char gMZPrefix
[] = "meta:";
39 static const char* KEYS
[] = {"lg", "ls", "ld", "sg", "ss", "sd"};
40 static const int32_t KEYS_SIZE
= (sizeof KEYS
/ sizeof KEYS
[0]);
42 static const char gEcTag
[] = "ec";
44 static const char EMPTY
[] = "<empty>"; // place holder for empty ZNames/TZNames
46 static const UTimeZoneNameType ALL_NAME_TYPES
[] = {
47 UTZNM_LONG_GENERIC
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
,
48 UTZNM_SHORT_GENERIC
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
,
49 UTZNM_UNKNOWN
// unknown as the last one
52 #define DEFAULT_CHARACTERNODE_CAPACITY 1
54 // ---------------------------------------------------
55 // CharacterNode class implementation
56 // ---------------------------------------------------
57 void CharacterNode::clear() {
58 uprv_memset(this, 0, sizeof(*this));
61 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
62 if (fValues
== NULL
) {
64 } else if (!fHasValuesVector
) {
66 valueDeleter(fValues
);
69 delete (UVector
*)fValues
;
74 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
75 if (U_FAILURE(status
)) {
81 if (fValues
== NULL
) {
84 // At least one value already.
85 if (!fHasValuesVector
) {
86 // There is only one value so far, and not in a vector yet.
87 // Create a vector and add the old value.
88 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
89 if (U_FAILURE(status
)) {
95 values
->addElement(fValues
, status
);
97 fHasValuesVector
= TRUE
;
100 ((UVector
*)fValues
)->addElement(value
, status
);
104 // ---------------------------------------------------
105 // TextTrieMapSearchResultHandler class implementation
106 // ---------------------------------------------------
107 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
110 // ---------------------------------------------------
111 // TextTrieMap class implementation
112 // ---------------------------------------------------
113 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
114 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
115 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
118 TextTrieMap::~TextTrieMap() {
120 for (index
= 0; index
< fNodesCount
; ++index
) {
121 fNodes
[index
].deleteValues(fValueDeleter
);
124 if (fLazyContents
!= NULL
) {
125 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
127 fValueDeleter(fLazyContents
->elementAt(i
+1));
130 delete fLazyContents
;
134 int32_t TextTrieMap::isEmpty() const {
135 // Use a separate field for fIsEmpty because it will remain unchanged once the
136 // Trie is built, while fNodes and fLazyContents change with the lazy init
137 // of the nodes structure. Trying to test the changing fields has
138 // thread safety complications.
143 // We defer actually building the TextTrieMap node structure until the first time a
144 // search is performed. put() simply saves the parameters in case we do
145 // eventually need to build it.
148 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
149 const UChar
*s
= sp
.get(key
, status
);
150 put(s
, value
, status
);
153 // This method is for designed for a persistent key, such as string key stored in
156 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
158 if (fLazyContents
== NULL
) {
159 fLazyContents
= new UVector(status
);
160 if (fLazyContents
== NULL
) {
161 status
= U_MEMORY_ALLOCATION_ERROR
;
164 if (U_FAILURE(status
)) {
167 U_ASSERT(fLazyContents
!= NULL
);
168 UChar
*s
= const_cast<UChar
*>(key
);
169 fLazyContents
->addElement(s
, status
);
170 fLazyContents
->addElement(value
, status
);
174 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
175 if (fNodes
== NULL
) {
176 fNodesCapacity
= 512;
177 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
178 fNodes
[0].clear(); // Init root node.
182 UnicodeString foldedKey
;
183 const UChar
*keyBuffer
;
186 // Ok to use fastCopyFrom() because we discard the copy when we return.
187 foldedKey
.fastCopyFrom(key
).foldCase();
188 keyBuffer
= foldedKey
.getBuffer();
189 keyLength
= foldedKey
.length();
191 keyBuffer
= key
.getBuffer();
192 keyLength
= key
.length();
195 CharacterNode
*node
= fNodes
;
197 for (index
= 0; index
< keyLength
; ++index
) {
198 node
= addChildNode(node
, keyBuffer
[index
], status
);
200 node
->addValue(value
, fValueDeleter
, status
);
204 TextTrieMap::growNodes() {
205 if (fNodesCapacity
== 0xffff) {
206 return FALSE
; // We use 16-bit node indexes.
208 int32_t newCapacity
= fNodesCapacity
+ 1000;
209 if (newCapacity
> 0xffff) {
210 newCapacity
= 0xffff;
212 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
213 if (newNodes
== NULL
) {
216 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
219 fNodesCapacity
= newCapacity
;
224 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
225 if (U_FAILURE(status
)) {
228 // Linear search of the sorted list of children.
229 uint16_t prevIndex
= 0;
230 uint16_t nodeIndex
= parent
->fFirstChild
;
231 while (nodeIndex
> 0) {
232 CharacterNode
*current
= fNodes
+ nodeIndex
;
233 UChar childCharacter
= current
->fCharacter
;
234 if (childCharacter
== c
) {
236 } else if (childCharacter
> c
) {
239 prevIndex
= nodeIndex
;
240 nodeIndex
= current
->fNextSibling
;
243 // Ensure capacity. Grow fNodes[] if needed.
244 if (fNodesCount
== fNodesCapacity
) {
245 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
247 status
= U_MEMORY_ALLOCATION_ERROR
;
250 parent
= fNodes
+ parentIndex
;
253 // Insert a new child node with c in sorted order.
254 CharacterNode
*node
= fNodes
+ fNodesCount
;
256 node
->fCharacter
= c
;
257 node
->fNextSibling
= nodeIndex
;
258 if (prevIndex
== 0) {
259 parent
->fFirstChild
= (uint16_t)fNodesCount
;
261 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
268 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
269 // Linear search of the sorted list of children.
270 uint16_t nodeIndex
= parent
->fFirstChild
;
271 while (nodeIndex
> 0) {
272 CharacterNode
*current
= fNodes
+ nodeIndex
;
273 UChar childCharacter
= current
->fCharacter
;
274 if (childCharacter
== c
) {
276 } else if (childCharacter
> c
) {
279 nodeIndex
= current
->fNextSibling
;
284 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
285 static UMTX TextTrieMutex
;
287 // buildTrie() - The Trie node structure is needed. Create it from the data that was
288 // saved at the time the ZoneStringFormatter was created. The Trie is only
289 // needed for parsing operations, which are less common than formatting,
290 // and the Trie is big, which is why its creation is deferred until first use.
291 void TextTrieMap::buildTrie(UErrorCode
&status
) {
292 umtx_lock(&TextTrieMutex
);
293 if (fLazyContents
!= NULL
) {
294 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
295 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
296 void *val
= fLazyContents
->elementAt(i
+1);
297 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
298 putImpl(keyString
, val
, status
);
300 delete fLazyContents
;
301 fLazyContents
= NULL
;
303 umtx_unlock(&TextTrieMutex
);
307 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
308 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
309 UBool trieNeedsInitialization
= FALSE
;
310 UMTX_CHECK(&TextTrieMutex
, fLazyContents
!= NULL
, trieNeedsInitialization
);
311 if (trieNeedsInitialization
) {
312 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
313 nonConstThis
->buildTrie(status
);
315 if (fNodes
== NULL
) {
318 search(fNodes
, text
, start
, start
, handler
, status
);
322 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
323 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
324 if (U_FAILURE(status
)) {
327 if (node
->hasValues()) {
328 if (!handler
->handleMatch(index
- start
, node
, status
)) {
331 if (U_FAILURE(status
)) {
335 UChar32 c
= text
.char32At(index
);
337 // size of character may grow after fold operation
338 UnicodeString
tmp(c
);
341 while (tmpidx
< tmp
.length()) {
342 c
= tmp
.char32At(tmpidx
);
343 node
= getChildNode(node
, c
);
347 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
350 node
= getChildNode(node
, c
);
353 search(node
, text
, start
, index
+1, handler
, status
);
357 // ---------------------------------------------------
358 // ZNStringPool class implementation
359 // ---------------------------------------------------
360 static const int32_t POOL_CHUNK_SIZE
= 2000;
361 struct ZNStringPoolChunk
: public UMemory
{
362 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
363 int32_t fLimit
; // Index to start of unused area at end of fStrings
364 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
368 ZNStringPoolChunk::ZNStringPoolChunk() {
373 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
376 if (U_FAILURE(status
)) {
379 fChunks
= new ZNStringPoolChunk
;
380 if (fChunks
== NULL
) {
381 status
= U_MEMORY_ALLOCATION_ERROR
;
385 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
386 uhash_compareUChars
/* keyComp */,
387 uhash_compareUChars
/* valueComp */,
389 if (U_FAILURE(status
)) {
394 ZNStringPool::~ZNStringPool() {
400 while (fChunks
!= NULL
) {
401 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
407 static const UChar EmptyString
= 0;
409 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
410 const UChar
*pooledString
;
411 if (U_FAILURE(status
)) {
415 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
416 if (pooledString
!= NULL
) {
420 int32_t length
= u_strlen(s
);
421 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
422 if (remainingLength
<= length
) {
423 U_ASSERT(length
< POOL_CHUNK_SIZE
);
424 if (length
>= POOL_CHUNK_SIZE
) {
425 status
= U_INTERNAL_PROGRAM_ERROR
;
428 ZNStringPoolChunk
*oldChunk
= fChunks
;
429 fChunks
= new ZNStringPoolChunk
;
430 if (fChunks
== NULL
) {
431 status
= U_MEMORY_ALLOCATION_ERROR
;
434 fChunks
->fNext
= oldChunk
;
437 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
438 u_strcpy(destString
, s
);
439 fChunks
->fLimit
+= (length
+ 1);
440 uhash_put(fHash
, destString
, destString
, &status
);
446 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
447 // into the pool's storage. Used for strings from resource bundles,
448 // which will perisist for the life of the zone string formatter, and
449 // therefore can be used directly without copying.
450 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
451 const UChar
*pooledString
;
452 if (U_FAILURE(status
)) {
456 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
457 if (pooledString
== NULL
) {
458 UChar
*ncs
= const_cast<UChar
*>(s
);
459 uhash_put(fHash
, ncs
, ncs
, &status
);
466 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
467 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
468 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
472 * freeze(). Close the hash table that maps to the pooled strings.
473 * After freezing, the pool can not be searched or added to,
474 * but all existing references to pooled strings remain valid.
476 * The main purpose is to recover the storage used for the hash.
478 void ZNStringPool::freeze() {
484 // ---------------------------------------------------
485 // ZNames - names common for time zone and meta zone
486 // ---------------------------------------------------
487 class ZNames
: public UMemory
{
491 static ZNames
* createInstance(UResourceBundle
* rb
, const char* key
);
492 const UChar
* getName(UTimeZoneNameType type
);
495 ZNames(const UChar
** names
);
496 static const UChar
** loadData(UResourceBundle
* rb
, const char* key
);
499 const UChar
** fNames
;
502 ZNames::ZNames(const UChar
** names
)
507 if (fNames
!= NULL
) {
513 ZNames::createInstance(UResourceBundle
* rb
, const char* key
) {
514 const UChar
** names
= loadData(rb
, key
);
516 // No names data available
519 return new ZNames(names
);
523 ZNames::getName(UTimeZoneNameType type
) {
524 if (fNames
== NULL
) {
527 const UChar
*name
= NULL
;
529 case UTZNM_LONG_GENERIC
:
532 case UTZNM_LONG_STANDARD
:
535 case UTZNM_LONG_DAYLIGHT
:
538 case UTZNM_SHORT_GENERIC
:
541 case UTZNM_SHORT_STANDARD
:
544 case UTZNM_SHORT_DAYLIGHT
:
554 ZNames::loadData(UResourceBundle
* rb
, const char* key
) {
555 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
559 UErrorCode status
= U_ZERO_ERROR
;
560 const UChar
**names
= NULL
;
562 UResourceBundle
* rbTable
= NULL
;
563 rbTable
= ures_getByKeyWithFallback(rb
, key
, rbTable
, &status
);
564 if (U_SUCCESS(status
)) {
565 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * KEYS_SIZE
);
567 UBool isEmpty
= TRUE
;
568 for (int32_t i
= 0; i
< KEYS_SIZE
; i
++) {
569 status
= U_ZERO_ERROR
;
571 const UChar
*value
= ures_getStringByKeyWithFallback(rbTable
, KEYS
[i
], &len
, &status
);
572 if (U_FAILURE(status
) || len
== 0) {
580 // No need to keep the names array
590 // ---------------------------------------------------
591 // TZNames - names for a time zone
592 // ---------------------------------------------------
593 class TZNames
: public ZNames
{
597 static TZNames
* createInstance(UResourceBundle
* rb
, const char* key
);
598 const UChar
* getLocationName(void);
601 TZNames(const UChar
** names
, const UChar
* locationName
);
602 const UChar
* fLocationName
;
605 TZNames::TZNames(const UChar
** names
, const UChar
* locationName
)
606 : ZNames(names
), fLocationName(locationName
) {
609 TZNames::~TZNames() {
613 TZNames::getLocationName() {
614 return fLocationName
;
618 TZNames::createInstance(UResourceBundle
* rb
, const char* key
) {
619 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
622 TZNames
* tznames
= NULL
;
623 UErrorCode status
= U_ZERO_ERROR
;
624 UResourceBundle
* rbTable
= ures_getByKeyWithFallback(rb
, key
, NULL
, &status
);
625 if (U_SUCCESS(status
)) {
627 const UChar
* locationName
= ures_getStringByKeyWithFallback(rbTable
, gEcTag
, &len
, &status
);
628 if (U_FAILURE(status
) || len
== 0) {
632 const UChar
** names
= loadData(rb
, key
);
634 if (locationName
!= NULL
|| names
!= NULL
) {
635 tznames
= new TZNames(names
, locationName
);
642 // ---------------------------------------------------
643 // The meta zone ID enumeration class
644 // ---------------------------------------------------
645 class MetaZoneIDsEnumeration
: public StringEnumeration
{
647 MetaZoneIDsEnumeration();
648 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
649 MetaZoneIDsEnumeration(UVector
* mzIDs
);
650 virtual ~MetaZoneIDsEnumeration();
651 static UClassID U_EXPORT2
getStaticClassID(void);
652 virtual UClassID
getDynamicClassID(void) const;
653 virtual const UnicodeString
* snext(UErrorCode
& status
);
654 virtual void reset(UErrorCode
& status
);
655 virtual int32_t count(UErrorCode
& status
) const;
659 const UVector
* fMetaZoneIDs
;
660 UVector
*fLocalVector
;
663 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
665 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
666 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
669 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
670 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
671 fLen
= fMetaZoneIDs
->size();
674 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
675 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
677 fLen
= fMetaZoneIDs
->size();
682 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
683 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
684 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
691 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
696 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
700 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
708 * ZNameInfo stores zone name information in the trie
710 typedef struct ZNameInfo
{
711 UTimeZoneNameType type
;
717 * ZMatchInfo stores zone name match information used by find method
719 typedef struct ZMatchInfo
{
720 const ZNameInfo
* znameInfo
;
726 // ---------------------------------------------------
727 // ZNameSearchHandler
728 // ---------------------------------------------------
729 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
731 ZNameSearchHandler(uint32_t types
);
732 virtual ~ZNameSearchHandler();
734 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
735 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
739 int32_t fMaxMatchLen
;
740 TimeZoneNames::MatchInfoCollection
* fResults
;
743 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
744 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
747 ZNameSearchHandler::~ZNameSearchHandler() {
748 if (fResults
!= NULL
) {
754 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
755 if (U_FAILURE(status
)) {
758 if (node
->hasValues()) {
759 int32_t valuesCount
= node
->countValues();
760 for (int32_t i
= 0; i
< valuesCount
; i
++) {
761 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
762 if (nameinfo
== NULL
) {
765 if ((nameinfo
->type
& fTypes
) != 0) {
766 // matches a requested type
767 if (fResults
== NULL
) {
768 fResults
= new TimeZoneNames::MatchInfoCollection();
769 if (fResults
== NULL
) {
770 status
= U_MEMORY_ALLOCATION_ERROR
;
773 if (U_SUCCESS(status
)) {
774 U_ASSERT(fResults
!= NULL
);
775 if (nameinfo
->tzID
) {
776 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
778 U_ASSERT(nameinfo
->mzID
);
779 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
781 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
782 fMaxMatchLen
= matchLength
;
791 TimeZoneNames::MatchInfoCollection
*
792 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
793 // give the ownership to the caller
794 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
795 maxMatchLen
= fMaxMatchLen
;
803 // ---------------------------------------------------
806 // TimeZoneNames implementation class. This is the main
807 // part of this module.
808 // ---------------------------------------------------
814 static void U_CALLCONV
815 deleteZNames(void *obj
) {
817 delete (ZNames
*)obj
;
821 * Deleter for TZNames
823 static void U_CALLCONV
824 deleteTZNames(void *obj
) {
826 delete (TZNames
*)obj
;
831 * Deleter for ZNameInfo
833 static void U_CALLCONV
834 deleteZNameInfo(void *obj
) {
840 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
846 fNamesTrieFullyLoaded(FALSE
),
847 fNamesTrie(TRUE
, deleteZNameInfo
) {
848 initialize(locale
, status
);
852 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
853 if (U_FAILURE(status
)) {
857 // Load zoneStrings bundle
858 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
859 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
860 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
861 if (U_FAILURE(tmpsts
)) {
867 // Initialize hashtables holding time zone/meta zone names
868 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
869 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
870 if (U_FAILURE(status
)) {
875 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
876 uhash_setValueDeleter(fTZNamesMap
, deleteTZNames
);
877 // no key deleters for name maps
879 // preload zone strings for the default zone
880 TimeZone
*tz
= TimeZone::createDefault();
881 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
883 loadStrings(UnicodeString(tzID
));
891 * This method updates the cache and must be called with a lock,
892 * except initializer.
895 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
) {
896 loadTimeZoneNames(tzCanonicalID
);
898 UErrorCode status
= U_ZERO_ERROR
;
899 StringEnumeration
*mzIDs
= getAvailableMetaZoneIDs(tzCanonicalID
, status
);
900 if (U_SUCCESS(status
) && mzIDs
!= NULL
) {
901 const UnicodeString
*mzID
;
902 while ((mzID
= mzIDs
->snext(status
))) {
903 if (U_FAILURE(status
)) {
906 loadMetaZoneNames(*mzID
);
912 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
914 umtx_destroy(&fLock
);
918 TimeZoneNamesImpl::cleanup() {
919 if (fZoneStrings
!= NULL
) {
920 ures_close(fZoneStrings
);
923 if (fMZNamesMap
!= NULL
) {
924 uhash_close(fMZNamesMap
);
927 if (fTZNamesMap
!= NULL
) {
928 uhash_close(fTZNamesMap
);
934 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
935 if (this == &other
) {
938 // No implementation for now
943 TimeZoneNamesImpl::clone() const {
944 UErrorCode status
= U_ZERO_ERROR
;
945 return new TimeZoneNamesImpl(fLocale
, status
);
949 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
950 if (U_FAILURE(status
)) {
953 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
955 return new MetaZoneIDsEnumeration();
957 return new MetaZoneIDsEnumeration(*mzIDs
);
961 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
962 if (U_FAILURE(status
)) {
965 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
966 if (mappings
== NULL
) {
967 return new MetaZoneIDsEnumeration();
970 MetaZoneIDsEnumeration
*senum
= NULL
;
971 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
973 status
= U_MEMORY_ALLOCATION_ERROR
;
975 if (U_SUCCESS(status
)) {
976 U_ASSERT(mzIDs
!= NULL
);
977 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
979 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
980 const UChar
*mzID
= map
->mzid
;
981 if (!mzIDs
->contains((void *)mzID
)) {
982 mzIDs
->addElement((void *)mzID
, status
);
985 if (U_SUCCESS(status
)) {
986 senum
= new MetaZoneIDsEnumeration(mzIDs
);
995 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
996 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1001 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1002 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1007 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1008 UTimeZoneNameType type
,
1009 UnicodeString
& name
) const {
1010 name
.setToBogus(); // cleanup result.
1011 if (mzID
.isEmpty()) {
1015 ZNames
*znames
= NULL
;
1016 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1018 umtx_lock(&nonConstThis
->fLock
);
1020 znames
= nonConstThis
->loadMetaZoneNames(mzID
);
1022 umtx_unlock(&nonConstThis
->fLock
);
1024 if (znames
!= NULL
) {
1025 const UChar
* s
= znames
->getName(type
);
1027 name
.setTo(TRUE
, s
, -1);
1034 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1035 name
.setToBogus(); // cleanup result.
1036 if (tzID
.isEmpty()) {
1040 TZNames
*tznames
= NULL
;
1041 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1043 umtx_lock(&nonConstThis
->fLock
);
1045 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1047 umtx_unlock(&nonConstThis
->fLock
);
1049 if (tznames
!= NULL
) {
1050 const UChar
*s
= tznames
->getName(type
);
1052 name
.setTo(TRUE
, s
, -1);
1059 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1060 const UChar
* locName
= NULL
;
1061 TZNames
*tznames
= NULL
;
1062 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1064 umtx_lock(&nonConstThis
->fLock
);
1066 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1068 umtx_unlock(&nonConstThis
->fLock
);
1070 if (tznames
!= NULL
) {
1071 locName
= tznames
->getLocationName();
1073 if (locName
!= NULL
) {
1074 name
.setTo(TRUE
, locName
, -1);
1078 return TimeZoneNames::getExemplarLocationName(tzID
, name
);
1082 // Merge the MZ_PREFIX and mzId
1083 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1084 if (mzID
.isEmpty()) {
1089 char mzIdChar
[ZID_KEY_MAX
+ 1];
1091 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1092 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1093 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1094 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1095 result
[keyLen
+ prefixLen
] = '\0';
1099 * This method updates the cache and must be called with a lock
1102 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
) {
1103 if (mzID
.length() > (ZID_KEY_MAX
- MZ_PREFIX_LEN
)) {
1107 ZNames
*znames
= NULL
;
1109 UErrorCode status
= U_ZERO_ERROR
;
1110 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1111 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1112 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1113 mzIDKey
[mzID
.length()] = 0;
1115 void *cacheVal
= uhash_get(fMZNamesMap
, mzIDKey
);
1116 if (cacheVal
== NULL
) {
1117 char key
[ZID_KEY_MAX
+ 1];
1118 mergeTimeZoneKey(mzID
, key
);
1119 znames
= ZNames::createInstance(fZoneStrings
, key
);
1121 if (znames
== NULL
) {
1122 cacheVal
= (void *)EMPTY
;
1126 // Use the persistent ID as the resource key, so we can
1127 // avoid duplications.
1128 const UChar
* newKey
= ZoneMeta::findMetaZoneID(mzID
);
1129 if (newKey
!= NULL
) {
1130 uhash_put(fMZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1131 if (U_FAILURE(status
)) {
1132 if (znames
!= NULL
) {
1135 } else if (znames
!= NULL
) {
1136 // put the name info into the trie
1137 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1138 const UChar
* name
= znames
->getName(ALL_NAME_TYPES
[i
]);
1140 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1141 if (nameinfo
!= NULL
) {
1142 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1143 nameinfo
->tzID
= NULL
;
1144 nameinfo
->mzID
= newKey
;
1145 fNamesTrie
.put(name
, nameinfo
, status
);
1152 // Should never happen with a valid input
1153 if (znames
!= NULL
) {
1154 // It's not possible that we get a valid ZNames with unknown ID.
1155 // But just in case..
1160 } else if (cacheVal
!= EMPTY
) {
1161 znames
= (ZNames
*)cacheVal
;
1168 * This method updates the cache and must be called with a lock
1171 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
) {
1172 if (tzID
.length() > ZID_KEY_MAX
) {
1176 TZNames
*tznames
= NULL
;
1178 UErrorCode status
= U_ZERO_ERROR
;
1179 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1180 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1181 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1182 tzIDKey
[tzIDKeyLen
] = 0;
1184 void *cacheVal
= uhash_get(fTZNamesMap
, tzIDKey
);
1185 if (cacheVal
== NULL
) {
1186 char key
[ZID_KEY_MAX
+ 1];
1187 UErrorCode status
= U_ZERO_ERROR
;
1188 // Replace "/" with ":".
1189 UnicodeString
uKey(tzID
);
1190 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
1191 if (uKey
.charAt(i
) == (UChar
)0x2F) {
1192 uKey
.setCharAt(i
, (UChar
)0x3A);
1195 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
1196 tznames
= TZNames::createInstance(fZoneStrings
, key
);
1198 if (tznames
== NULL
) {
1199 cacheVal
= (void *)EMPTY
;
1203 // Use the persistent ID as the resource key, so we can
1204 // avoid duplications.
1205 const UChar
* newKey
= ZoneMeta::findTimeZoneID(tzID
);
1206 if (newKey
!= NULL
) {
1207 uhash_put(fTZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1208 if (U_FAILURE(status
)) {
1209 if (tznames
!= NULL
) {
1212 } else if (tznames
!= NULL
) {
1213 // put the name info into the trie
1214 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1215 const UChar
* name
= tznames
->getName(ALL_NAME_TYPES
[i
]);
1217 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1218 if (nameinfo
!= NULL
) {
1219 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1220 nameinfo
->tzID
= newKey
;
1221 nameinfo
->mzID
= NULL
;
1222 fNamesTrie
.put(name
, nameinfo
, status
);
1228 // Should never happen with a valid input
1229 if (tznames
!= NULL
) {
1230 // It's not possible that we get a valid TZNames with unknown ID.
1231 // But just in case..
1236 } else if (cacheVal
!= EMPTY
) {
1237 tznames
= (TZNames
*)cacheVal
;
1243 TimeZoneNames::MatchInfoCollection
*
1244 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1245 ZNameSearchHandler
handler(types
);
1247 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1249 umtx_lock(&nonConstThis
->fLock
);
1251 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1253 umtx_unlock(&nonConstThis
->fLock
);
1255 if (U_FAILURE(status
)) {
1260 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1261 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1268 // All names are not yet loaded into the trie
1269 umtx_lock(&nonConstThis
->fLock
);
1271 if (!fNamesTrieFullyLoaded
) {
1272 const UnicodeString
*id
;
1274 // load strings for all zones
1275 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1276 if (U_SUCCESS(status
)) {
1277 while ((id
= tzIDs
->snext(status
))) {
1278 if (U_FAILURE(status
)) {
1281 // loadStrings also load related metazone strings
1282 nonConstThis
->loadStrings(*id
);
1285 if (tzIDs
!= NULL
) {
1288 if (U_SUCCESS(status
)) {
1289 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1293 umtx_unlock(&nonConstThis
->fLock
);
1295 if (U_FAILURE(status
)) {
1299 umtx_lock(&nonConstThis
->fLock
);
1302 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1304 umtx_unlock(&nonConstThis
->fLock
);
1306 return handler
.getMatches(maxLen
);
1312 #endif /* #if !UCONFIG_NO_FORMATTING */