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 */