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"
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_EXEMPLAR_LOCATION
,
50 UTZNM_UNKNOWN
// unknown as the last one
53 #define DEFAULT_CHARACTERNODE_CAPACITY 1
55 // ---------------------------------------------------
56 // CharacterNode class implementation
57 // ---------------------------------------------------
58 void CharacterNode::clear() {
59 uprv_memset(this, 0, sizeof(*this));
62 void CharacterNode::deleteValues(UObjectDeleter
*valueDeleter
) {
63 if (fValues
== NULL
) {
65 } else if (!fHasValuesVector
) {
67 valueDeleter(fValues
);
70 delete (UVector
*)fValues
;
75 CharacterNode::addValue(void *value
, UObjectDeleter
*valueDeleter
, UErrorCode
&status
) {
76 if (U_FAILURE(status
)) {
82 if (fValues
== NULL
) {
85 // At least one value already.
86 if (!fHasValuesVector
) {
87 // There is only one value so far, and not in a vector yet.
88 // Create a vector and add the old value.
89 UVector
*values
= new UVector(valueDeleter
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
90 if (U_FAILURE(status
)) {
96 values
->addElement(fValues
, status
);
98 fHasValuesVector
= TRUE
;
100 // Add the new value.
101 ((UVector
*)fValues
)->addElement(value
, status
);
105 // ---------------------------------------------------
106 // TextTrieMapSearchResultHandler class implementation
107 // ---------------------------------------------------
108 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
111 // ---------------------------------------------------
112 // TextTrieMap class implementation
113 // ---------------------------------------------------
114 TextTrieMap::TextTrieMap(UBool ignoreCase
, UObjectDeleter
*valueDeleter
)
115 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
116 fLazyContents(NULL
), fIsEmpty(TRUE
), fValueDeleter(valueDeleter
) {
119 TextTrieMap::~TextTrieMap() {
121 for (index
= 0; index
< fNodesCount
; ++index
) {
122 fNodes
[index
].deleteValues(fValueDeleter
);
125 if (fLazyContents
!= NULL
) {
126 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
128 fValueDeleter(fLazyContents
->elementAt(i
+1));
131 delete fLazyContents
;
135 int32_t TextTrieMap::isEmpty() const {
136 // Use a separate field for fIsEmpty because it will remain unchanged once the
137 // Trie is built, while fNodes and fLazyContents change with the lazy init
138 // of the nodes structure. Trying to test the changing fields has
139 // thread safety complications.
144 // We defer actually building the TextTrieMap node structure until the first time a
145 // search is performed. put() simply saves the parameters in case we do
146 // eventually need to build it.
149 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZNStringPool
&sp
, UErrorCode
&status
) {
150 const UChar
*s
= sp
.get(key
, status
);
151 put(s
, value
, status
);
154 // This method is for designed for a persistent key, such as string key stored in
157 TextTrieMap::put(const UChar
*key
, void *value
, UErrorCode
&status
) {
159 if (fLazyContents
== NULL
) {
160 fLazyContents
= new UVector(status
);
161 if (fLazyContents
== NULL
) {
162 status
= U_MEMORY_ALLOCATION_ERROR
;
165 if (U_FAILURE(status
)) {
168 U_ASSERT(fLazyContents
!= NULL
);
169 UChar
*s
= const_cast<UChar
*>(key
);
170 fLazyContents
->addElement(s
, status
);
171 fLazyContents
->addElement(value
, status
);
175 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
176 if (fNodes
== NULL
) {
177 fNodesCapacity
= 512;
178 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
179 fNodes
[0].clear(); // Init root node.
183 UnicodeString foldedKey
;
184 const UChar
*keyBuffer
;
187 // Ok to use fastCopyFrom() because we discard the copy when we return.
188 foldedKey
.fastCopyFrom(key
).foldCase();
189 keyBuffer
= foldedKey
.getBuffer();
190 keyLength
= foldedKey
.length();
192 keyBuffer
= key
.getBuffer();
193 keyLength
= key
.length();
196 CharacterNode
*node
= fNodes
;
198 for (index
= 0; index
< keyLength
; ++index
) {
199 node
= addChildNode(node
, keyBuffer
[index
], status
);
201 node
->addValue(value
, fValueDeleter
, status
);
205 TextTrieMap::growNodes() {
206 if (fNodesCapacity
== 0xffff) {
207 return FALSE
; // We use 16-bit node indexes.
209 int32_t newCapacity
= fNodesCapacity
+ 1000;
210 if (newCapacity
> 0xffff) {
211 newCapacity
= 0xffff;
213 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
214 if (newNodes
== NULL
) {
217 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
220 fNodesCapacity
= newCapacity
;
225 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
226 if (U_FAILURE(status
)) {
229 // Linear search of the sorted list of children.
230 uint16_t prevIndex
= 0;
231 uint16_t nodeIndex
= parent
->fFirstChild
;
232 while (nodeIndex
> 0) {
233 CharacterNode
*current
= fNodes
+ nodeIndex
;
234 UChar childCharacter
= current
->fCharacter
;
235 if (childCharacter
== c
) {
237 } else if (childCharacter
> c
) {
240 prevIndex
= nodeIndex
;
241 nodeIndex
= current
->fNextSibling
;
244 // Ensure capacity. Grow fNodes[] if needed.
245 if (fNodesCount
== fNodesCapacity
) {
246 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
248 status
= U_MEMORY_ALLOCATION_ERROR
;
251 parent
= fNodes
+ parentIndex
;
254 // Insert a new child node with c in sorted order.
255 CharacterNode
*node
= fNodes
+ fNodesCount
;
257 node
->fCharacter
= c
;
258 node
->fNextSibling
= nodeIndex
;
259 if (prevIndex
== 0) {
260 parent
->fFirstChild
= (uint16_t)fNodesCount
;
262 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
269 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
270 // Linear search of the sorted list of children.
271 uint16_t nodeIndex
= parent
->fFirstChild
;
272 while (nodeIndex
> 0) {
273 CharacterNode
*current
= fNodes
+ nodeIndex
;
274 UChar childCharacter
= current
->fCharacter
;
275 if (childCharacter
== c
) {
277 } else if (childCharacter
> c
) {
280 nodeIndex
= current
->fNextSibling
;
285 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
286 static UMutex TextTrieMutex
= U_MUTEX_INITIALIZER
;
288 // buildTrie() - The Trie node structure is needed. Create it from the data that was
289 // saved at the time the ZoneStringFormatter was created. The Trie is only
290 // needed for parsing operations, which are less common than formatting,
291 // and the Trie is big, which is why its creation is deferred until first use.
292 void TextTrieMap::buildTrie(UErrorCode
&status
) {
293 umtx_lock(&TextTrieMutex
);
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
;
304 umtx_unlock(&TextTrieMutex
);
308 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
309 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
310 UBool trieNeedsInitialization
= FALSE
;
311 UMTX_CHECK(&TextTrieMutex
, fLazyContents
!= NULL
, trieNeedsInitialization
);
312 if (trieNeedsInitialization
) {
313 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
314 nonConstThis
->buildTrie(status
);
316 if (fNodes
== NULL
) {
319 search(fNodes
, text
, start
, start
, handler
, status
);
323 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
324 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
325 if (U_FAILURE(status
)) {
328 if (node
->hasValues()) {
329 if (!handler
->handleMatch(index
- start
, node
, status
)) {
332 if (U_FAILURE(status
)) {
336 UChar32 c
= text
.char32At(index
);
338 // size of character may grow after fold operation
339 UnicodeString
tmp(c
);
342 while (tmpidx
< tmp
.length()) {
343 c
= tmp
.char32At(tmpidx
);
344 node
= getChildNode(node
, c
);
348 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
351 node
= getChildNode(node
, c
);
354 search(node
, text
, start
, index
+1, handler
, status
);
358 // ---------------------------------------------------
359 // ZNStringPool class implementation
360 // ---------------------------------------------------
361 static const int32_t POOL_CHUNK_SIZE
= 2000;
362 struct ZNStringPoolChunk
: public UMemory
{
363 ZNStringPoolChunk
*fNext
; // Ptr to next pool chunk
364 int32_t fLimit
; // Index to start of unused area at end of fStrings
365 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
369 ZNStringPoolChunk::ZNStringPoolChunk() {
374 ZNStringPool::ZNStringPool(UErrorCode
&status
) {
377 if (U_FAILURE(status
)) {
380 fChunks
= new ZNStringPoolChunk
;
381 if (fChunks
== NULL
) {
382 status
= U_MEMORY_ALLOCATION_ERROR
;
386 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
387 uhash_compareUChars
/* keyComp */,
388 uhash_compareUChars
/* valueComp */,
390 if (U_FAILURE(status
)) {
395 ZNStringPool::~ZNStringPool() {
401 while (fChunks
!= NULL
) {
402 ZNStringPoolChunk
*nextChunk
= fChunks
->fNext
;
408 static const UChar EmptyString
= 0;
410 const UChar
*ZNStringPool::get(const UChar
*s
, UErrorCode
&status
) {
411 const UChar
*pooledString
;
412 if (U_FAILURE(status
)) {
416 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
417 if (pooledString
!= NULL
) {
421 int32_t length
= u_strlen(s
);
422 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
423 if (remainingLength
<= length
) {
424 U_ASSERT(length
< POOL_CHUNK_SIZE
);
425 if (length
>= POOL_CHUNK_SIZE
) {
426 status
= U_INTERNAL_PROGRAM_ERROR
;
429 ZNStringPoolChunk
*oldChunk
= fChunks
;
430 fChunks
= new ZNStringPoolChunk
;
431 if (fChunks
== NULL
) {
432 status
= U_MEMORY_ALLOCATION_ERROR
;
435 fChunks
->fNext
= oldChunk
;
438 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
439 u_strcpy(destString
, s
);
440 fChunks
->fLimit
+= (length
+ 1);
441 uhash_put(fHash
, destString
, destString
, &status
);
447 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
448 // into the pool's storage. Used for strings from resource bundles,
449 // which will perisist for the life of the zone string formatter, and
450 // therefore can be used directly without copying.
451 const UChar
*ZNStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
452 const UChar
*pooledString
;
453 if (U_FAILURE(status
)) {
457 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
458 if (pooledString
== NULL
) {
459 UChar
*ncs
= const_cast<UChar
*>(s
);
460 uhash_put(fHash
, ncs
, ncs
, &status
);
467 const UChar
*ZNStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
468 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
469 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
473 * freeze(). Close the hash table that maps to the pooled strings.
474 * After freezing, the pool can not be searched or added to,
475 * but all existing references to pooled strings remain valid.
477 * The main purpose is to recover the storage used for the hash.
479 void ZNStringPool::freeze() {
485 // ---------------------------------------------------
486 // ZNames - names common for time zone and meta zone
487 // ---------------------------------------------------
488 class ZNames
: public UMemory
{
492 static ZNames
* createInstance(UResourceBundle
* rb
, const char* key
);
493 virtual const UChar
* getName(UTimeZoneNameType type
);
496 ZNames(const UChar
** names
);
497 static const UChar
** loadData(UResourceBundle
* rb
, const char* key
);
500 const UChar
** fNames
;
503 ZNames::ZNames(const UChar
** names
)
508 if (fNames
!= NULL
) {
514 ZNames::createInstance(UResourceBundle
* rb
, const char* key
) {
515 const UChar
** names
= loadData(rb
, key
);
517 // No names data available
520 return new ZNames(names
);
524 ZNames::getName(UTimeZoneNameType type
) {
525 if (fNames
== NULL
) {
528 const UChar
*name
= NULL
;
530 case UTZNM_LONG_GENERIC
:
533 case UTZNM_LONG_STANDARD
:
536 case UTZNM_LONG_DAYLIGHT
:
539 case UTZNM_SHORT_GENERIC
:
542 case UTZNM_SHORT_STANDARD
:
545 case UTZNM_SHORT_DAYLIGHT
:
548 case UTZNM_EXEMPLAR_LOCATION
: // implemeted by subclass
556 ZNames::loadData(UResourceBundle
* rb
, const char* key
) {
557 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
561 UErrorCode status
= U_ZERO_ERROR
;
562 const UChar
**names
= NULL
;
564 UResourceBundle
* rbTable
= NULL
;
565 rbTable
= ures_getByKeyWithFallback(rb
, key
, rbTable
, &status
);
566 if (U_SUCCESS(status
)) {
567 names
= (const UChar
**)uprv_malloc(sizeof(const UChar
*) * KEYS_SIZE
);
569 UBool isEmpty
= TRUE
;
570 for (int32_t i
= 0; i
< KEYS_SIZE
; i
++) {
571 status
= U_ZERO_ERROR
;
573 const UChar
*value
= ures_getStringByKeyWithFallback(rbTable
, KEYS
[i
], &len
, &status
);
574 if (U_FAILURE(status
) || len
== 0) {
582 // No need to keep the names array
592 // ---------------------------------------------------
593 // TZNames - names for a time zone
594 // ---------------------------------------------------
595 class TZNames
: public ZNames
{
599 static TZNames
* createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
);
600 virtual const UChar
* getName(UTimeZoneNameType type
);
603 TZNames(const UChar
** names
);
604 const UChar
* fLocationName
;
605 UChar
* fLocationNameOwned
;
608 TZNames::TZNames(const UChar
** names
)
609 : ZNames(names
), fLocationName(NULL
), fLocationNameOwned(NULL
) {
612 TZNames::~TZNames() {
613 if (fLocationNameOwned
) {
614 uprv_free(fLocationNameOwned
);
619 TZNames::getName(UTimeZoneNameType type
) {
620 if (type
== UTZNM_EXEMPLAR_LOCATION
) {
621 return fLocationName
;
623 return ZNames::getName(type
);
627 TZNames::createInstance(UResourceBundle
* rb
, const char* key
, const UnicodeString
& tzID
) {
628 if (rb
== NULL
|| key
== NULL
|| *key
== 0) {
632 const UChar
** names
= loadData(rb
, key
);
633 const UChar
* locationName
= NULL
;
634 UChar
* locationNameOwned
= NULL
;
636 UErrorCode status
= U_ZERO_ERROR
;
639 UResourceBundle
* table
= ures_getByKeyWithFallback(rb
, key
, NULL
, &status
);
640 locationName
= ures_getStringByKeyWithFallback(table
, gEcTag
, &len
, &status
);
641 // ignore missing resource here
642 status
= U_ZERO_ERROR
;
646 if (locationName
== NULL
) {
647 UnicodeString tmpName
;
648 int32_t tmpNameLen
= 0;
649 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, tmpName
);
650 tmpNameLen
= tmpName
.length();
652 if (tmpNameLen
> 0) {
653 locationNameOwned
= (UChar
*) uprv_malloc(sizeof(UChar
) * (tmpNameLen
+ 1));
654 if (locationNameOwned
) {
655 tmpName
.extract(locationNameOwned
, tmpNameLen
+ 1, status
);
656 locationName
= locationNameOwned
;
661 TZNames
* tznames
= NULL
;
662 if (locationName
!= NULL
|| names
!= NULL
) {
663 tznames
= new TZNames(names
);
664 if (tznames
== NULL
) {
665 if (locationNameOwned
) {
666 uprv_free(locationNameOwned
);
669 tznames
->fLocationName
= locationName
;
670 tznames
->fLocationNameOwned
= locationNameOwned
;
676 // ---------------------------------------------------
677 // The meta zone ID enumeration class
678 // ---------------------------------------------------
679 class MetaZoneIDsEnumeration
: public StringEnumeration
{
681 MetaZoneIDsEnumeration();
682 MetaZoneIDsEnumeration(const UVector
& mzIDs
);
683 MetaZoneIDsEnumeration(UVector
* mzIDs
);
684 virtual ~MetaZoneIDsEnumeration();
685 static UClassID U_EXPORT2
getStaticClassID(void);
686 virtual UClassID
getDynamicClassID(void) const;
687 virtual const UnicodeString
* snext(UErrorCode
& status
);
688 virtual void reset(UErrorCode
& status
);
689 virtual int32_t count(UErrorCode
& status
) const;
693 const UVector
* fMetaZoneIDs
;
694 UVector
*fLocalVector
;
697 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration
)
699 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
700 : fLen(0), fPos(0), fMetaZoneIDs(NULL
), fLocalVector(NULL
) {
703 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector
& mzIDs
)
704 : fPos(0), fMetaZoneIDs(&mzIDs
), fLocalVector(NULL
) {
705 fLen
= fMetaZoneIDs
->size();
708 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector
*mzIDs
)
709 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs
), fLocalVector(mzIDs
) {
711 fLen
= fMetaZoneIDs
->size();
716 MetaZoneIDsEnumeration::snext(UErrorCode
& status
) {
717 if (U_SUCCESS(status
) && fMetaZoneIDs
!= NULL
&& fPos
< fLen
) {
718 unistr
.setTo((const UChar
*)fMetaZoneIDs
->elementAt(fPos
++), -1);
725 MetaZoneIDsEnumeration::reset(UErrorCode
& /*status*/) {
730 MetaZoneIDsEnumeration::count(UErrorCode
& /*status*/) const {
734 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
742 * ZNameInfo stores zone name information in the trie
744 typedef struct ZNameInfo
{
745 UTimeZoneNameType type
;
751 * ZMatchInfo stores zone name match information used by find method
753 typedef struct ZMatchInfo
{
754 const ZNameInfo
* znameInfo
;
760 // ---------------------------------------------------
761 // ZNameSearchHandler
762 // ---------------------------------------------------
763 class ZNameSearchHandler
: public TextTrieMapSearchResultHandler
{
765 ZNameSearchHandler(uint32_t types
);
766 virtual ~ZNameSearchHandler();
768 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
769 TimeZoneNames::MatchInfoCollection
* getMatches(int32_t& maxMatchLen
);
773 int32_t fMaxMatchLen
;
774 TimeZoneNames::MatchInfoCollection
* fResults
;
777 ZNameSearchHandler::ZNameSearchHandler(uint32_t types
)
778 : fTypes(types
), fMaxMatchLen(0), fResults(NULL
) {
781 ZNameSearchHandler::~ZNameSearchHandler() {
782 if (fResults
!= NULL
) {
788 ZNameSearchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
789 if (U_FAILURE(status
)) {
792 if (node
->hasValues()) {
793 int32_t valuesCount
= node
->countValues();
794 for (int32_t i
= 0; i
< valuesCount
; i
++) {
795 ZNameInfo
*nameinfo
= (ZNameInfo
*)node
->getValue(i
);
796 if (nameinfo
== NULL
) {
799 if ((nameinfo
->type
& fTypes
) != 0) {
800 // matches a requested type
801 if (fResults
== NULL
) {
802 fResults
= new TimeZoneNames::MatchInfoCollection();
803 if (fResults
== NULL
) {
804 status
= U_MEMORY_ALLOCATION_ERROR
;
807 if (U_SUCCESS(status
)) {
808 U_ASSERT(fResults
!= NULL
);
809 if (nameinfo
->tzID
) {
810 fResults
->addZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->tzID
, -1), status
);
812 U_ASSERT(nameinfo
->mzID
);
813 fResults
->addMetaZone(nameinfo
->type
, matchLength
, UnicodeString(nameinfo
->mzID
, -1), status
);
815 if (U_SUCCESS(status
) && matchLength
> fMaxMatchLen
) {
816 fMaxMatchLen
= matchLength
;
825 TimeZoneNames::MatchInfoCollection
*
826 ZNameSearchHandler::getMatches(int32_t& maxMatchLen
) {
827 // give the ownership to the caller
828 TimeZoneNames::MatchInfoCollection
* results
= fResults
;
829 maxMatchLen
= fMaxMatchLen
;
837 // ---------------------------------------------------
840 // TimeZoneNames implementation class. This is the main
841 // part of this module.
842 // ---------------------------------------------------
848 static void U_CALLCONV
849 deleteZNames(void *obj
) {
851 delete (ZNames
*)obj
;
855 * Deleter for TZNames
857 static void U_CALLCONV
858 deleteTZNames(void *obj
) {
860 delete (TZNames
*)obj
;
865 * Deleter for ZNameInfo
867 static void U_CALLCONV
868 deleteZNameInfo(void *obj
) {
874 static UMutex gLock
= U_MUTEX_INITIALIZER
;
876 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale
& locale
, UErrorCode
& status
)
881 fNamesTrieFullyLoaded(FALSE
),
882 fNamesTrie(TRUE
, deleteZNameInfo
) {
883 initialize(locale
, status
);
887 TimeZoneNamesImpl::initialize(const Locale
& locale
, UErrorCode
& status
) {
888 if (U_FAILURE(status
)) {
892 // Load zoneStrings bundle
893 UErrorCode tmpsts
= U_ZERO_ERROR
; // OK with fallback warning..
894 fZoneStrings
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &tmpsts
);
895 fZoneStrings
= ures_getByKeyWithFallback(fZoneStrings
, gZoneStrings
, fZoneStrings
, &tmpsts
);
896 if (U_FAILURE(tmpsts
)) {
902 // Initialize hashtables holding time zone/meta zone names
903 fMZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
904 fTZNamesMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
905 if (U_FAILURE(status
)) {
910 uhash_setValueDeleter(fMZNamesMap
, deleteZNames
);
911 uhash_setValueDeleter(fTZNamesMap
, deleteTZNames
);
912 // no key deleters for name maps
914 // preload zone strings for the default zone
915 TimeZone
*tz
= TimeZone::createDefault();
916 const UChar
*tzID
= ZoneMeta::getCanonicalCLDRID(*tz
);
918 loadStrings(UnicodeString(tzID
));
926 * This method updates the cache and must be called with a lock,
927 * except initializer.
930 TimeZoneNamesImpl::loadStrings(const UnicodeString
& tzCanonicalID
) {
931 loadTimeZoneNames(tzCanonicalID
);
933 UErrorCode status
= U_ZERO_ERROR
;
934 StringEnumeration
*mzIDs
= getAvailableMetaZoneIDs(tzCanonicalID
, status
);
935 if (U_SUCCESS(status
) && mzIDs
!= NULL
) {
936 const UnicodeString
*mzID
;
937 while ((mzID
= mzIDs
->snext(status
))) {
938 if (U_FAILURE(status
)) {
941 loadMetaZoneNames(*mzID
);
947 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
952 TimeZoneNamesImpl::cleanup() {
953 if (fZoneStrings
!= NULL
) {
954 ures_close(fZoneStrings
);
957 if (fMZNamesMap
!= NULL
) {
958 uhash_close(fMZNamesMap
);
961 if (fTZNamesMap
!= NULL
) {
962 uhash_close(fTZNamesMap
);
968 TimeZoneNamesImpl::operator==(const TimeZoneNames
& other
) const {
969 if (this == &other
) {
972 // No implementation for now
977 TimeZoneNamesImpl::clone() const {
978 UErrorCode status
= U_ZERO_ERROR
;
979 return new TimeZoneNamesImpl(fLocale
, status
);
983 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
984 if (U_FAILURE(status
)) {
987 const UVector
* mzIDs
= ZoneMeta::getAvailableMetazoneIDs();
989 return new MetaZoneIDsEnumeration();
991 return new MetaZoneIDsEnumeration(*mzIDs
);
995 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
996 if (U_FAILURE(status
)) {
999 const UVector
* mappings
= ZoneMeta::getMetazoneMappings(tzID
);
1000 if (mappings
== NULL
) {
1001 return new MetaZoneIDsEnumeration();
1004 MetaZoneIDsEnumeration
*senum
= NULL
;
1005 UVector
* mzIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
1006 if (mzIDs
== NULL
) {
1007 status
= U_MEMORY_ALLOCATION_ERROR
;
1009 if (U_SUCCESS(status
)) {
1010 U_ASSERT(mzIDs
!= NULL
);
1011 for (int32_t i
= 0; U_SUCCESS(status
) && i
< mappings
->size(); i
++) {
1013 OlsonToMetaMappingEntry
*map
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
1014 const UChar
*mzID
= map
->mzid
;
1015 if (!mzIDs
->contains((void *)mzID
)) {
1016 mzIDs
->addElement((void *)mzID
, status
);
1019 if (U_SUCCESS(status
)) {
1020 senum
= new MetaZoneIDsEnumeration(mzIDs
);
1029 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
1030 ZoneMeta::getMetazoneID(tzID
, date
, mzID
);
1035 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
1036 ZoneMeta::getZoneIdByMetazone(mzID
, UnicodeString(region
, -1, US_INV
), tzID
);
1041 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString
& mzID
,
1042 UTimeZoneNameType type
,
1043 UnicodeString
& name
) const {
1044 name
.setToBogus(); // cleanup result.
1045 if (mzID
.isEmpty()) {
1049 ZNames
*znames
= NULL
;
1050 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1054 znames
= nonConstThis
->loadMetaZoneNames(mzID
);
1056 umtx_unlock(&gLock
);
1058 if (znames
!= NULL
) {
1059 const UChar
* s
= znames
->getName(type
);
1061 name
.setTo(TRUE
, s
, -1);
1068 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
1069 name
.setToBogus(); // cleanup result.
1070 if (tzID
.isEmpty()) {
1074 TZNames
*tznames
= NULL
;
1075 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1079 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1081 umtx_unlock(&gLock
);
1083 if (tznames
!= NULL
) {
1084 const UChar
*s
= tznames
->getName(type
);
1086 name
.setTo(TRUE
, s
, -1);
1093 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
1094 name
.setToBogus(); // cleanup result.
1095 const UChar
* locName
= NULL
;
1096 TZNames
*tznames
= NULL
;
1097 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1101 tznames
= nonConstThis
->loadTimeZoneNames(tzID
);
1103 umtx_unlock(&gLock
);
1105 if (tznames
!= NULL
) {
1106 locName
= tznames
->getName(UTZNM_EXEMPLAR_LOCATION
);
1108 if (locName
!= NULL
) {
1109 name
.setTo(TRUE
, locName
, -1);
1116 // Merge the MZ_PREFIX and mzId
1117 static void mergeTimeZoneKey(const UnicodeString
& mzID
, char* result
) {
1118 if (mzID
.isEmpty()) {
1123 char mzIdChar
[ZID_KEY_MAX
+ 1];
1125 int32_t prefixLen
= uprv_strlen(gMZPrefix
);
1126 keyLen
= mzID
.extract(0, mzID
.length(), mzIdChar
, ZID_KEY_MAX
+ 1, US_INV
);
1127 uprv_memcpy((void *)result
, (void *)gMZPrefix
, prefixLen
);
1128 uprv_memcpy((void *)(result
+ prefixLen
), (void *)mzIdChar
, keyLen
);
1129 result
[keyLen
+ prefixLen
] = '\0';
1133 * This method updates the cache and must be called with a lock
1136 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString
& mzID
) {
1137 if (mzID
.length() > (ZID_KEY_MAX
- MZ_PREFIX_LEN
)) {
1141 ZNames
*znames
= NULL
;
1143 UErrorCode status
= U_ZERO_ERROR
;
1144 UChar mzIDKey
[ZID_KEY_MAX
+ 1];
1145 mzID
.extract(mzIDKey
, ZID_KEY_MAX
+ 1, status
);
1146 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1147 mzIDKey
[mzID
.length()] = 0;
1149 void *cacheVal
= uhash_get(fMZNamesMap
, mzIDKey
);
1150 if (cacheVal
== NULL
) {
1151 char key
[ZID_KEY_MAX
+ 1];
1152 mergeTimeZoneKey(mzID
, key
);
1153 znames
= ZNames::createInstance(fZoneStrings
, key
);
1155 if (znames
== NULL
) {
1156 cacheVal
= (void *)EMPTY
;
1160 // Use the persistent ID as the resource key, so we can
1161 // avoid duplications.
1162 const UChar
* newKey
= ZoneMeta::findMetaZoneID(mzID
);
1163 if (newKey
!= NULL
) {
1164 uhash_put(fMZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1165 if (U_FAILURE(status
)) {
1166 if (znames
!= NULL
) {
1169 } else if (znames
!= NULL
) {
1170 // put the name info into the trie
1171 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1172 const UChar
* name
= znames
->getName(ALL_NAME_TYPES
[i
]);
1174 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1175 if (nameinfo
!= NULL
) {
1176 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1177 nameinfo
->tzID
= NULL
;
1178 nameinfo
->mzID
= newKey
;
1179 fNamesTrie
.put(name
, nameinfo
, status
);
1186 // Should never happen with a valid input
1187 if (znames
!= NULL
) {
1188 // It's not possible that we get a valid ZNames with unknown ID.
1189 // But just in case..
1194 } else if (cacheVal
!= EMPTY
) {
1195 znames
= (ZNames
*)cacheVal
;
1202 * This method updates the cache and must be called with a lock
1205 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString
& tzID
) {
1206 if (tzID
.length() > ZID_KEY_MAX
) {
1210 TZNames
*tznames
= NULL
;
1212 UErrorCode status
= U_ZERO_ERROR
;
1213 UChar tzIDKey
[ZID_KEY_MAX
+ 1];
1214 int32_t tzIDKeyLen
= tzID
.extract(tzIDKey
, ZID_KEY_MAX
+ 1, status
);
1215 U_ASSERT(status
== U_ZERO_ERROR
); // already checked length above
1216 tzIDKey
[tzIDKeyLen
] = 0;
1218 void *cacheVal
= uhash_get(fTZNamesMap
, tzIDKey
);
1219 if (cacheVal
== NULL
) {
1220 char key
[ZID_KEY_MAX
+ 1];
1221 UErrorCode status
= U_ZERO_ERROR
;
1222 // Replace "/" with ":".
1223 UnicodeString
uKey(tzID
);
1224 for (int32_t i
= 0; i
< uKey
.length(); i
++) {
1225 if (uKey
.charAt(i
) == (UChar
)0x2F) {
1226 uKey
.setCharAt(i
, (UChar
)0x3A);
1229 uKey
.extract(0, uKey
.length(), key
, sizeof(key
), US_INV
);
1230 tznames
= TZNames::createInstance(fZoneStrings
, key
, tzID
);
1232 if (tznames
== NULL
) {
1233 cacheVal
= (void *)EMPTY
;
1237 // Use the persistent ID as the resource key, so we can
1238 // avoid duplications.
1239 const UChar
* newKey
= ZoneMeta::findTimeZoneID(tzID
);
1240 if (newKey
!= NULL
) {
1241 uhash_put(fTZNamesMap
, (void *)newKey
, cacheVal
, &status
);
1242 if (U_FAILURE(status
)) {
1243 if (tznames
!= NULL
) {
1246 } else if (tznames
!= NULL
) {
1247 // put the name info into the trie
1248 for (int32_t i
= 0; ALL_NAME_TYPES
[i
] != UTZNM_UNKNOWN
; i
++) {
1249 const UChar
* name
= tznames
->getName(ALL_NAME_TYPES
[i
]);
1251 ZNameInfo
*nameinfo
= (ZNameInfo
*)uprv_malloc(sizeof(ZNameInfo
));
1252 if (nameinfo
!= NULL
) {
1253 nameinfo
->type
= ALL_NAME_TYPES
[i
];
1254 nameinfo
->tzID
= newKey
;
1255 nameinfo
->mzID
= NULL
;
1256 fNamesTrie
.put(name
, nameinfo
, status
);
1262 // Should never happen with a valid input
1263 if (tznames
!= NULL
) {
1264 // It's not possible that we get a valid TZNames with unknown ID.
1265 // But just in case..
1270 } else if (cacheVal
!= EMPTY
) {
1271 tznames
= (TZNames
*)cacheVal
;
1277 TimeZoneNames::MatchInfoCollection
*
1278 TimeZoneNamesImpl::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
1279 ZNameSearchHandler
handler(types
);
1281 TimeZoneNamesImpl
*nonConstThis
= const_cast<TimeZoneNamesImpl
*>(this);
1285 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1287 umtx_unlock(&gLock
);
1289 if (U_FAILURE(status
)) {
1294 TimeZoneNames::MatchInfoCollection
* matches
= handler
.getMatches(maxLen
);
1295 if (matches
!= NULL
&& ((maxLen
== (text
.length() - start
)) || fNamesTrieFullyLoaded
)) {
1302 // All names are not yet loaded into the trie
1305 if (!fNamesTrieFullyLoaded
) {
1306 const UnicodeString
*id
;
1308 // load strings for all zones
1309 StringEnumeration
*tzIDs
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
1310 if (U_SUCCESS(status
)) {
1311 while ((id
= tzIDs
->snext(status
))) {
1312 if (U_FAILURE(status
)) {
1315 // loadStrings also load related metazone strings
1316 nonConstThis
->loadStrings(*id
);
1319 if (tzIDs
!= NULL
) {
1322 if (U_SUCCESS(status
)) {
1323 nonConstThis
->fNamesTrieFullyLoaded
= TRUE
;
1327 umtx_unlock(&gLock
);
1329 if (U_FAILURE(status
)) {
1336 fNamesTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1338 umtx_unlock(&gLock
);
1340 return handler
.getMatches(maxLen
);
1343 static const UChar gEtcPrefix
[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1344 static const int32_t gEtcPrefixLen
= 4;
1345 static const UChar gSystemVPrefix
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1346 static const int32_t gSystemVPrefixLen
= 8;
1347 static const UChar gRiyadh8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1348 static const int32_t gRiyadh8Len
= 7;
1350 UnicodeString
& U_EXPORT2
1351 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) {
1352 if (tzID
.isEmpty() || tzID
.startsWith(gEtcPrefix
, gEtcPrefixLen
)
1353 || tzID
.startsWith(gSystemVPrefix
, gSystemVPrefixLen
) || tzID
.indexOf(gRiyadh8
, gRiyadh8Len
, 0) > 0) {
1358 int32_t sep
= tzID
.lastIndexOf((UChar
)0x2F /* '/' */);
1359 if (sep
> 0 && sep
+ 1 < tzID
.length()) {
1360 name
.setTo(tzID
, sep
+ 1);
1361 name
.findAndReplace(UnicodeString((UChar
)0x5f /* _ */),
1362 UnicodeString((UChar
)0x20 /* space */));
1372 #endif /* #if !UCONFIG_NO_FORMATTING */