michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 2011-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: * michael@0: * File TZNAMES_IMPL.CPP michael@0: * michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/timezone.h" michael@0: michael@0: #include "tznames_impl.h" michael@0: #include "cmemory.h" michael@0: #include "cstring.h" michael@0: #include "uassert.h" michael@0: #include "mutex.h" michael@0: #include "uresimp.h" michael@0: #include "ureslocs.h" michael@0: #include "zonemeta.h" michael@0: #include "ucln_in.h" michael@0: #include "uvector.h" michael@0: #include "olsontz.h" michael@0: michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: #define ZID_KEY_MAX 128 michael@0: #define MZ_PREFIX_LEN 5 michael@0: michael@0: static const char gZoneStrings[] = "zoneStrings"; michael@0: static const char gMZPrefix[] = "meta:"; michael@0: michael@0: static const char* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"}; michael@0: static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]); michael@0: michael@0: static const char gEcTag[] = "ec"; michael@0: michael@0: static const char EMPTY[] = ""; // place holder for empty ZNames/TZNames michael@0: michael@0: static const UTimeZoneNameType ALL_NAME_TYPES[] = { michael@0: UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, michael@0: UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, michael@0: UTZNM_EXEMPLAR_LOCATION, michael@0: UTZNM_UNKNOWN // unknown as the last one michael@0: }; michael@0: michael@0: #define DEFAULT_CHARACTERNODE_CAPACITY 1 michael@0: michael@0: // --------------------------------------------------- michael@0: // CharacterNode class implementation michael@0: // --------------------------------------------------- michael@0: void CharacterNode::clear() { michael@0: uprv_memset(this, 0, sizeof(*this)); michael@0: } michael@0: michael@0: void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) { michael@0: if (fValues == NULL) { michael@0: // Do nothing. michael@0: } else if (!fHasValuesVector) { michael@0: if (valueDeleter) { michael@0: valueDeleter(fValues); michael@0: } michael@0: } else { michael@0: delete (UVector *)fValues; michael@0: } michael@0: } michael@0: michael@0: void michael@0: CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: if (valueDeleter) { michael@0: valueDeleter(value); michael@0: } michael@0: return; michael@0: } michael@0: if (fValues == NULL) { michael@0: fValues = value; michael@0: } else { michael@0: // At least one value already. michael@0: if (!fHasValuesVector) { michael@0: // There is only one value so far, and not in a vector yet. michael@0: // Create a vector and add the old value. michael@0: UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); michael@0: if (U_FAILURE(status)) { michael@0: if (valueDeleter) { michael@0: valueDeleter(value); michael@0: } michael@0: return; michael@0: } michael@0: values->addElement(fValues, status); michael@0: fValues = values; michael@0: fHasValuesVector = TRUE; michael@0: } michael@0: // Add the new value. michael@0: ((UVector *)fValues)->addElement(value, status); michael@0: } michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // TextTrieMapSearchResultHandler class implementation michael@0: // --------------------------------------------------- michael@0: TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // TextTrieMap class implementation michael@0: // --------------------------------------------------- michael@0: TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter) michael@0: : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0), michael@0: fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) { michael@0: } michael@0: michael@0: TextTrieMap::~TextTrieMap() { michael@0: int32_t index; michael@0: for (index = 0; index < fNodesCount; ++index) { michael@0: fNodes[index].deleteValues(fValueDeleter); michael@0: } michael@0: uprv_free(fNodes); michael@0: if (fLazyContents != NULL) { michael@0: for (int32_t i=0; isize(); i+=2) { michael@0: if (fValueDeleter) { michael@0: fValueDeleter(fLazyContents->elementAt(i+1)); michael@0: } michael@0: } michael@0: delete fLazyContents; michael@0: } michael@0: } michael@0: michael@0: int32_t TextTrieMap::isEmpty() const { michael@0: // Use a separate field for fIsEmpty because it will remain unchanged once the michael@0: // Trie is built, while fNodes and fLazyContents change with the lazy init michael@0: // of the nodes structure. Trying to test the changing fields has michael@0: // thread safety complications. michael@0: return fIsEmpty; michael@0: } michael@0: michael@0: michael@0: // We defer actually building the TextTrieMap node structure until the first time a michael@0: // search is performed. put() simply saves the parameters in case we do michael@0: // eventually need to build it. michael@0: // michael@0: void michael@0: TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) { michael@0: const UChar *s = sp.get(key, status); michael@0: put(s, value, status); michael@0: } michael@0: michael@0: // This method is for designed for a persistent key, such as string key stored in michael@0: // resource bundle. michael@0: void michael@0: TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { michael@0: fIsEmpty = FALSE; michael@0: if (fLazyContents == NULL) { michael@0: fLazyContents = new UVector(status); michael@0: if (fLazyContents == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: U_ASSERT(fLazyContents != NULL); michael@0: UChar *s = const_cast(key); michael@0: fLazyContents->addElement(s, status); michael@0: fLazyContents->addElement(value, status); michael@0: } michael@0: michael@0: void michael@0: TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) { michael@0: if (fNodes == NULL) { michael@0: fNodesCapacity = 512; michael@0: fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); michael@0: fNodes[0].clear(); // Init root node. michael@0: fNodesCount = 1; michael@0: } michael@0: michael@0: UnicodeString foldedKey; michael@0: const UChar *keyBuffer; michael@0: int32_t keyLength; michael@0: if (fIgnoreCase) { michael@0: // Ok to use fastCopyFrom() because we discard the copy when we return. michael@0: foldedKey.fastCopyFrom(key).foldCase(); michael@0: keyBuffer = foldedKey.getBuffer(); michael@0: keyLength = foldedKey.length(); michael@0: } else { michael@0: keyBuffer = key.getBuffer(); michael@0: keyLength = key.length(); michael@0: } michael@0: michael@0: CharacterNode *node = fNodes; michael@0: int32_t index; michael@0: for (index = 0; index < keyLength; ++index) { michael@0: node = addChildNode(node, keyBuffer[index], status); michael@0: } michael@0: node->addValue(value, fValueDeleter, status); michael@0: } michael@0: michael@0: UBool michael@0: TextTrieMap::growNodes() { michael@0: if (fNodesCapacity == 0xffff) { michael@0: return FALSE; // We use 16-bit node indexes. michael@0: } michael@0: int32_t newCapacity = fNodesCapacity + 1000; michael@0: if (newCapacity > 0xffff) { michael@0: newCapacity = 0xffff; michael@0: } michael@0: CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode)); michael@0: if (newNodes == NULL) { michael@0: return FALSE; michael@0: } michael@0: uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); michael@0: uprv_free(fNodes); michael@0: fNodes = newNodes; michael@0: fNodesCapacity = newCapacity; michael@0: return TRUE; michael@0: } michael@0: michael@0: CharacterNode* michael@0: TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: // Linear search of the sorted list of children. michael@0: uint16_t prevIndex = 0; michael@0: uint16_t nodeIndex = parent->fFirstChild; michael@0: while (nodeIndex > 0) { michael@0: CharacterNode *current = fNodes + nodeIndex; michael@0: UChar childCharacter = current->fCharacter; michael@0: if (childCharacter == c) { michael@0: return current; michael@0: } else if (childCharacter > c) { michael@0: break; michael@0: } michael@0: prevIndex = nodeIndex; michael@0: nodeIndex = current->fNextSibling; michael@0: } michael@0: michael@0: // Ensure capacity. Grow fNodes[] if needed. michael@0: if (fNodesCount == fNodesCapacity) { michael@0: int32_t parentIndex = (int32_t)(parent - fNodes); michael@0: if (!growNodes()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: parent = fNodes + parentIndex; michael@0: } michael@0: michael@0: // Insert a new child node with c in sorted order. michael@0: CharacterNode *node = fNodes + fNodesCount; michael@0: node->clear(); michael@0: node->fCharacter = c; michael@0: node->fNextSibling = nodeIndex; michael@0: if (prevIndex == 0) { michael@0: parent->fFirstChild = (uint16_t)fNodesCount; michael@0: } else { michael@0: fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount; michael@0: } michael@0: ++fNodesCount; michael@0: return node; michael@0: } michael@0: michael@0: CharacterNode* michael@0: TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const { michael@0: // Linear search of the sorted list of children. michael@0: uint16_t nodeIndex = parent->fFirstChild; michael@0: while (nodeIndex > 0) { michael@0: CharacterNode *current = fNodes + nodeIndex; michael@0: UChar childCharacter = current->fCharacter; michael@0: if (childCharacter == c) { michael@0: return current; michael@0: } else if (childCharacter > c) { michael@0: break; michael@0: } michael@0: nodeIndex = current->fNextSibling; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: // Mutex for protecting the lazy creation of the Trie node structure on the first call to search(). michael@0: static UMutex TextTrieMutex = U_MUTEX_INITIALIZER; michael@0: michael@0: // buildTrie() - The Trie node structure is needed. Create it from the data that was michael@0: // saved at the time the ZoneStringFormatter was created. The Trie is only michael@0: // needed for parsing operations, which are less common than formatting, michael@0: // and the Trie is big, which is why its creation is deferred until first use. michael@0: void TextTrieMap::buildTrie(UErrorCode &status) { michael@0: if (fLazyContents != NULL) { michael@0: for (int32_t i=0; isize(); i+=2) { michael@0: const UChar *key = (UChar *)fLazyContents->elementAt(i); michael@0: void *val = fLazyContents->elementAt(i+1); michael@0: UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor. michael@0: putImpl(keyString, val, status); michael@0: } michael@0: delete fLazyContents; michael@0: fLazyContents = NULL; michael@0: } michael@0: } michael@0: michael@0: void michael@0: TextTrieMap::search(const UnicodeString &text, int32_t start, michael@0: TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { michael@0: { michael@0: // TODO: if locking the mutex for each check proves to be a performance problem, michael@0: // add a flag of type atomic_int32_t to class TextTrieMap, and use only michael@0: // the ICU atomic safe functions for assigning and testing. michael@0: // Don't test the pointer fLazyContents. michael@0: // Don't do unless it's really required. michael@0: Mutex lock(&TextTrieMutex); michael@0: if (fLazyContents != NULL) { michael@0: TextTrieMap *nonConstThis = const_cast(this); michael@0: nonConstThis->buildTrie(status); michael@0: } michael@0: } michael@0: if (fNodes == NULL) { michael@0: return; michael@0: } michael@0: search(fNodes, text, start, start, handler, status); michael@0: } michael@0: michael@0: void michael@0: TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, michael@0: int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: if (node->hasValues()) { michael@0: if (!handler->handleMatch(index - start, node, status)) { michael@0: return; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: } michael@0: UChar32 c = text.char32At(index); michael@0: if (fIgnoreCase) { michael@0: // size of character may grow after fold operation michael@0: UnicodeString tmp(c); michael@0: tmp.foldCase(); michael@0: int32_t tmpidx = 0; michael@0: while (tmpidx < tmp.length()) { michael@0: c = tmp.char32At(tmpidx); michael@0: node = getChildNode(node, c); michael@0: if (node == NULL) { michael@0: break; michael@0: } michael@0: tmpidx = tmp.moveIndex32(tmpidx, 1); michael@0: } michael@0: } else { michael@0: node = getChildNode(node, c); michael@0: } michael@0: if (node != NULL) { michael@0: search(node, text, start, index+1, handler, status); michael@0: } michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // ZNStringPool class implementation michael@0: // --------------------------------------------------- michael@0: static const int32_t POOL_CHUNK_SIZE = 2000; michael@0: struct ZNStringPoolChunk: public UMemory { michael@0: ZNStringPoolChunk *fNext; // Ptr to next pool chunk michael@0: int32_t fLimit; // Index to start of unused area at end of fStrings michael@0: UChar fStrings[POOL_CHUNK_SIZE]; // Strings array michael@0: ZNStringPoolChunk(); michael@0: }; michael@0: michael@0: ZNStringPoolChunk::ZNStringPoolChunk() { michael@0: fNext = NULL; michael@0: fLimit = 0; michael@0: } michael@0: michael@0: ZNStringPool::ZNStringPool(UErrorCode &status) { michael@0: fChunks = NULL; michael@0: fHash = NULL; michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: fChunks = new ZNStringPoolChunk; michael@0: if (fChunks == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: michael@0: fHash = uhash_open(uhash_hashUChars /* keyHash */, michael@0: uhash_compareUChars /* keyComp */, michael@0: uhash_compareUChars /* valueComp */, michael@0: &status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: ZNStringPool::~ZNStringPool() { michael@0: if (fHash != NULL) { michael@0: uhash_close(fHash); michael@0: fHash = NULL; michael@0: } michael@0: michael@0: while (fChunks != NULL) { michael@0: ZNStringPoolChunk *nextChunk = fChunks->fNext; michael@0: delete fChunks; michael@0: fChunks = nextChunk; michael@0: } michael@0: } michael@0: michael@0: static const UChar EmptyString = 0; michael@0: michael@0: const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) { michael@0: const UChar *pooledString; michael@0: if (U_FAILURE(status)) { michael@0: return &EmptyString; michael@0: } michael@0: michael@0: pooledString = static_cast(uhash_get(fHash, s)); michael@0: if (pooledString != NULL) { michael@0: return pooledString; michael@0: } michael@0: michael@0: int32_t length = u_strlen(s); michael@0: int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit; michael@0: if (remainingLength <= length) { michael@0: U_ASSERT(length < POOL_CHUNK_SIZE); michael@0: if (length >= POOL_CHUNK_SIZE) { michael@0: status = U_INTERNAL_PROGRAM_ERROR; michael@0: return &EmptyString; michael@0: } michael@0: ZNStringPoolChunk *oldChunk = fChunks; michael@0: fChunks = new ZNStringPoolChunk; michael@0: if (fChunks == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return &EmptyString; michael@0: } michael@0: fChunks->fNext = oldChunk; michael@0: } michael@0: michael@0: UChar *destString = &fChunks->fStrings[fChunks->fLimit]; michael@0: u_strcpy(destString, s); michael@0: fChunks->fLimit += (length + 1); michael@0: uhash_put(fHash, destString, destString, &status); michael@0: return destString; michael@0: } michael@0: michael@0: michael@0: // michael@0: // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data michael@0: // into the pool's storage. Used for strings from resource bundles, michael@0: // which will perisist for the life of the zone string formatter, and michael@0: // therefore can be used directly without copying. michael@0: const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) { michael@0: const UChar *pooledString; michael@0: if (U_FAILURE(status)) { michael@0: return &EmptyString; michael@0: } michael@0: if (s != NULL) { michael@0: pooledString = static_cast(uhash_get(fHash, s)); michael@0: if (pooledString == NULL) { michael@0: UChar *ncs = const_cast(s); michael@0: uhash_put(fHash, ncs, ncs, &status); michael@0: } michael@0: } michael@0: return s; michael@0: } michael@0: michael@0: michael@0: const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) { michael@0: UnicodeString &nonConstStr = const_cast(s); michael@0: return this->get(nonConstStr.getTerminatedBuffer(), status); michael@0: } michael@0: michael@0: /* michael@0: * freeze(). Close the hash table that maps to the pooled strings. michael@0: * After freezing, the pool can not be searched or added to, michael@0: * but all existing references to pooled strings remain valid. michael@0: * michael@0: * The main purpose is to recover the storage used for the hash. michael@0: */ michael@0: void ZNStringPool::freeze() { michael@0: uhash_close(fHash); michael@0: fHash = NULL; michael@0: } michael@0: michael@0: michael@0: // --------------------------------------------------- michael@0: // ZNames - names common for time zone and meta zone michael@0: // --------------------------------------------------- michael@0: class ZNames : public UMemory { michael@0: public: michael@0: virtual ~ZNames(); michael@0: michael@0: static ZNames* createInstance(UResourceBundle* rb, const char* key); michael@0: virtual const UChar* getName(UTimeZoneNameType type); michael@0: michael@0: protected: michael@0: ZNames(const UChar** names); michael@0: static const UChar** loadData(UResourceBundle* rb, const char* key); michael@0: michael@0: private: michael@0: const UChar** fNames; michael@0: }; michael@0: michael@0: ZNames::ZNames(const UChar** names) michael@0: : fNames(names) { michael@0: } michael@0: michael@0: ZNames::~ZNames() { michael@0: if (fNames != NULL) { michael@0: uprv_free(fNames); michael@0: } michael@0: } michael@0: michael@0: ZNames* michael@0: ZNames::createInstance(UResourceBundle* rb, const char* key) { michael@0: const UChar** names = loadData(rb, key); michael@0: if (names == NULL) { michael@0: // No names data available michael@0: return NULL; michael@0: } michael@0: return new ZNames(names); michael@0: } michael@0: michael@0: const UChar* michael@0: ZNames::getName(UTimeZoneNameType type) { michael@0: if (fNames == NULL) { michael@0: return NULL; michael@0: } michael@0: const UChar *name = NULL; michael@0: switch(type) { michael@0: case UTZNM_LONG_GENERIC: michael@0: name = fNames[0]; michael@0: break; michael@0: case UTZNM_LONG_STANDARD: michael@0: name = fNames[1]; michael@0: break; michael@0: case UTZNM_LONG_DAYLIGHT: michael@0: name = fNames[2]; michael@0: break; michael@0: case UTZNM_SHORT_GENERIC: michael@0: name = fNames[3]; michael@0: break; michael@0: case UTZNM_SHORT_STANDARD: michael@0: name = fNames[4]; michael@0: break; michael@0: case UTZNM_SHORT_DAYLIGHT: michael@0: name = fNames[5]; michael@0: break; michael@0: case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass michael@0: default: michael@0: name = NULL; michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: const UChar** michael@0: ZNames::loadData(UResourceBundle* rb, const char* key) { michael@0: if (rb == NULL || key == NULL || *key == 0) { michael@0: return NULL; michael@0: } michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: const UChar **names = NULL; michael@0: michael@0: UResourceBundle* rbTable = NULL; michael@0: rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status); michael@0: if (U_SUCCESS(status)) { michael@0: names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE); michael@0: if (names != NULL) { michael@0: UBool isEmpty = TRUE; michael@0: for (int32_t i = 0; i < KEYS_SIZE; i++) { michael@0: status = U_ZERO_ERROR; michael@0: int32_t len = 0; michael@0: const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status); michael@0: if (U_FAILURE(status) || len == 0) { michael@0: names[i] = NULL; michael@0: } else { michael@0: names[i] = value; michael@0: isEmpty = FALSE; michael@0: } michael@0: } michael@0: if (isEmpty) { michael@0: // No need to keep the names array michael@0: uprv_free(names); michael@0: names = NULL; michael@0: } michael@0: } michael@0: } michael@0: ures_close(rbTable); michael@0: return names; michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // TZNames - names for a time zone michael@0: // --------------------------------------------------- michael@0: class TZNames : public ZNames { michael@0: public: michael@0: virtual ~TZNames(); michael@0: michael@0: static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID); michael@0: virtual const UChar* getName(UTimeZoneNameType type); michael@0: michael@0: private: michael@0: TZNames(const UChar** names); michael@0: const UChar* fLocationName; michael@0: UChar* fLocationNameOwned; michael@0: }; michael@0: michael@0: TZNames::TZNames(const UChar** names) michael@0: : ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) { michael@0: } michael@0: michael@0: TZNames::~TZNames() { michael@0: if (fLocationNameOwned) { michael@0: uprv_free(fLocationNameOwned); michael@0: } michael@0: } michael@0: michael@0: const UChar* michael@0: TZNames::getName(UTimeZoneNameType type) { michael@0: if (type == UTZNM_EXEMPLAR_LOCATION) { michael@0: return fLocationName; michael@0: } michael@0: return ZNames::getName(type); michael@0: } michael@0: michael@0: TZNames* michael@0: TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) { michael@0: if (rb == NULL || key == NULL || *key == 0) { michael@0: return NULL; michael@0: } michael@0: michael@0: const UChar** names = loadData(rb, key); michael@0: const UChar* locationName = NULL; michael@0: UChar* locationNameOwned = NULL; michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t len = 0; michael@0: michael@0: UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status); michael@0: locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status); michael@0: // ignore missing resource here michael@0: status = U_ZERO_ERROR; michael@0: michael@0: ures_close(table); michael@0: michael@0: if (locationName == NULL) { michael@0: UnicodeString tmpName; michael@0: int32_t tmpNameLen = 0; michael@0: TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName); michael@0: tmpNameLen = tmpName.length(); michael@0: michael@0: if (tmpNameLen > 0) { michael@0: locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1)); michael@0: if (locationNameOwned) { michael@0: tmpName.extract(locationNameOwned, tmpNameLen + 1, status); michael@0: locationName = locationNameOwned; michael@0: } michael@0: } michael@0: } michael@0: michael@0: TZNames* tznames = NULL; michael@0: if (locationName != NULL || names != NULL) { michael@0: tznames = new TZNames(names); michael@0: if (tznames == NULL) { michael@0: if (locationNameOwned) { michael@0: uprv_free(locationNameOwned); michael@0: } michael@0: } michael@0: tznames->fLocationName = locationName; michael@0: tznames->fLocationNameOwned = locationNameOwned; michael@0: } michael@0: michael@0: return tznames; michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // The meta zone ID enumeration class michael@0: // --------------------------------------------------- michael@0: class MetaZoneIDsEnumeration : public StringEnumeration { michael@0: public: michael@0: MetaZoneIDsEnumeration(); michael@0: MetaZoneIDsEnumeration(const UVector& mzIDs); michael@0: MetaZoneIDsEnumeration(UVector* mzIDs); michael@0: virtual ~MetaZoneIDsEnumeration(); michael@0: static UClassID U_EXPORT2 getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: virtual const UnicodeString* snext(UErrorCode& status); michael@0: virtual void reset(UErrorCode& status); michael@0: virtual int32_t count(UErrorCode& status) const; michael@0: private: michael@0: int32_t fLen; michael@0: int32_t fPos; michael@0: const UVector* fMetaZoneIDs; michael@0: UVector *fLocalVector; michael@0: }; michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration) michael@0: michael@0: MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() michael@0: : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) { michael@0: } michael@0: michael@0: MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) michael@0: : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) { michael@0: fLen = fMetaZoneIDs->size(); michael@0: } michael@0: michael@0: MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs) michael@0: : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) { michael@0: if (fMetaZoneIDs) { michael@0: fLen = fMetaZoneIDs->size(); michael@0: } michael@0: } michael@0: michael@0: const UnicodeString* michael@0: MetaZoneIDsEnumeration::snext(UErrorCode& status) { michael@0: if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) { michael@0: unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1); michael@0: return &unistr; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void michael@0: MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) { michael@0: fPos = 0; michael@0: } michael@0: michael@0: int32_t michael@0: MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const { michael@0: return fLen; michael@0: } michael@0: michael@0: MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() { michael@0: if (fLocalVector) { michael@0: delete fLocalVector; michael@0: } michael@0: } michael@0: michael@0: U_CDECL_BEGIN michael@0: /** michael@0: * ZNameInfo stores zone name information in the trie michael@0: */ michael@0: typedef struct ZNameInfo { michael@0: UTimeZoneNameType type; michael@0: const UChar* tzID; michael@0: const UChar* mzID; michael@0: } ZNameInfo; michael@0: michael@0: /** michael@0: * ZMatchInfo stores zone name match information used by find method michael@0: */ michael@0: typedef struct ZMatchInfo { michael@0: const ZNameInfo* znameInfo; michael@0: int32_t matchLength; michael@0: } ZMatchInfo; michael@0: U_CDECL_END michael@0: michael@0: michael@0: // --------------------------------------------------- michael@0: // ZNameSearchHandler michael@0: // --------------------------------------------------- michael@0: class ZNameSearchHandler : public TextTrieMapSearchResultHandler { michael@0: public: michael@0: ZNameSearchHandler(uint32_t types); michael@0: virtual ~ZNameSearchHandler(); michael@0: michael@0: UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); michael@0: TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen); michael@0: michael@0: private: michael@0: uint32_t fTypes; michael@0: int32_t fMaxMatchLen; michael@0: TimeZoneNames::MatchInfoCollection* fResults; michael@0: }; michael@0: michael@0: ZNameSearchHandler::ZNameSearchHandler(uint32_t types) michael@0: : fTypes(types), fMaxMatchLen(0), fResults(NULL) { michael@0: } michael@0: michael@0: ZNameSearchHandler::~ZNameSearchHandler() { michael@0: if (fResults != NULL) { michael@0: delete fResults; michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: if (node->hasValues()) { michael@0: int32_t valuesCount = node->countValues(); michael@0: for (int32_t i = 0; i < valuesCount; i++) { michael@0: ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); michael@0: if (nameinfo == NULL) { michael@0: break; michael@0: } michael@0: if ((nameinfo->type & fTypes) != 0) { michael@0: // matches a requested type michael@0: if (fResults == NULL) { michael@0: fResults = new TimeZoneNames::MatchInfoCollection(); michael@0: if (fResults == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: if (U_SUCCESS(status)) { michael@0: U_ASSERT(fResults != NULL); michael@0: if (nameinfo->tzID) { michael@0: fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status); michael@0: } else { michael@0: U_ASSERT(nameinfo->mzID); michael@0: fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status); michael@0: } michael@0: if (U_SUCCESS(status) && matchLength > fMaxMatchLen) { michael@0: fMaxMatchLen = matchLength; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: TimeZoneNames::MatchInfoCollection* michael@0: ZNameSearchHandler::getMatches(int32_t& maxMatchLen) { michael@0: // give the ownership to the caller michael@0: TimeZoneNames::MatchInfoCollection* results = fResults; michael@0: maxMatchLen = fMaxMatchLen; michael@0: michael@0: // reset michael@0: fResults = NULL; michael@0: fMaxMatchLen = 0; michael@0: return results; michael@0: } michael@0: michael@0: // --------------------------------------------------- michael@0: // TimeZoneNamesImpl michael@0: // michael@0: // TimeZoneNames implementation class. This is the main michael@0: // part of this module. michael@0: // --------------------------------------------------- michael@0: michael@0: U_CDECL_BEGIN michael@0: /** michael@0: * Deleter for ZNames michael@0: */ michael@0: static void U_CALLCONV michael@0: deleteZNames(void *obj) { michael@0: if (obj != EMPTY) { michael@0: delete (ZNames *)obj; michael@0: } michael@0: } michael@0: /** michael@0: * Deleter for TZNames michael@0: */ michael@0: static void U_CALLCONV michael@0: deleteTZNames(void *obj) { michael@0: if (obj != EMPTY) { michael@0: delete (TZNames *)obj; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Deleter for ZNameInfo michael@0: */ michael@0: static void U_CALLCONV michael@0: deleteZNameInfo(void *obj) { michael@0: uprv_free(obj); michael@0: } michael@0: michael@0: U_CDECL_END michael@0: michael@0: static UMutex gLock = U_MUTEX_INITIALIZER; michael@0: michael@0: TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status) michael@0: : fLocale(locale), michael@0: fZoneStrings(NULL), michael@0: fTZNamesMap(NULL), michael@0: fMZNamesMap(NULL), michael@0: fNamesTrieFullyLoaded(FALSE), michael@0: fNamesTrie(TRUE, deleteZNameInfo) { michael@0: initialize(locale, status); michael@0: } michael@0: michael@0: void michael@0: TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: // Load zoneStrings bundle michael@0: UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. michael@0: fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); michael@0: fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts); michael@0: if (U_FAILURE(tmpsts)) { michael@0: status = tmpsts; michael@0: cleanup(); michael@0: return; michael@0: } michael@0: michael@0: // Initialize hashtables holding time zone/meta zone names michael@0: fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); michael@0: fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); michael@0: if (U_FAILURE(status)) { michael@0: cleanup(); michael@0: return; michael@0: } michael@0: michael@0: uhash_setValueDeleter(fMZNamesMap, deleteZNames); michael@0: uhash_setValueDeleter(fTZNamesMap, deleteTZNames); michael@0: // no key deleters for name maps michael@0: michael@0: // preload zone strings for the default zone michael@0: TimeZone *tz = TimeZone::createDefault(); michael@0: const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); michael@0: if (tzID != NULL) { michael@0: loadStrings(UnicodeString(tzID)); michael@0: } michael@0: delete tz; michael@0: michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: * This method updates the cache and must be called with a lock, michael@0: * except initializer. michael@0: */ michael@0: void michael@0: TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) { michael@0: loadTimeZoneNames(tzCanonicalID); michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status); michael@0: if (U_SUCCESS(status) && mzIDs != NULL) { michael@0: const UnicodeString *mzID; michael@0: while ((mzID = mzIDs->snext(status))) { michael@0: if (U_FAILURE(status)) { michael@0: break; michael@0: } michael@0: loadMetaZoneNames(*mzID); michael@0: } michael@0: delete mzIDs; michael@0: } michael@0: } michael@0: michael@0: TimeZoneNamesImpl::~TimeZoneNamesImpl() { michael@0: cleanup(); michael@0: } michael@0: michael@0: void michael@0: TimeZoneNamesImpl::cleanup() { michael@0: if (fZoneStrings != NULL) { michael@0: ures_close(fZoneStrings); michael@0: fZoneStrings = NULL; michael@0: } michael@0: if (fMZNamesMap != NULL) { michael@0: uhash_close(fMZNamesMap); michael@0: fMZNamesMap = NULL; michael@0: } michael@0: if (fTZNamesMap != NULL) { michael@0: uhash_close(fTZNamesMap); michael@0: fTZNamesMap = NULL; michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const { michael@0: if (this == &other) { michael@0: return TRUE; michael@0: } michael@0: // No implementation for now michael@0: return FALSE; michael@0: } michael@0: michael@0: TimeZoneNames* michael@0: TimeZoneNamesImpl::clone() const { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: return new TimeZoneNamesImpl(fLocale, status); michael@0: } michael@0: michael@0: StringEnumeration* michael@0: TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs(); michael@0: if (mzIDs == NULL) { michael@0: return new MetaZoneIDsEnumeration(); michael@0: } michael@0: return new MetaZoneIDsEnumeration(*mzIDs); michael@0: } michael@0: michael@0: StringEnumeration* michael@0: TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID); michael@0: if (mappings == NULL) { michael@0: return new MetaZoneIDsEnumeration(); michael@0: } michael@0: michael@0: MetaZoneIDsEnumeration *senum = NULL; michael@0: UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status); michael@0: if (mzIDs == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: if (U_SUCCESS(status)) { michael@0: U_ASSERT(mzIDs != NULL); michael@0: for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) { michael@0: michael@0: OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i); michael@0: const UChar *mzID = map->mzid; michael@0: if (!mzIDs->contains((void *)mzID)) { michael@0: mzIDs->addElement((void *)mzID, status); michael@0: } michael@0: } michael@0: if (U_SUCCESS(status)) { michael@0: senum = new MetaZoneIDsEnumeration(mzIDs); michael@0: } else { michael@0: delete mzIDs; michael@0: } michael@0: } michael@0: return senum; michael@0: } michael@0: michael@0: UnicodeString& michael@0: TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { michael@0: ZoneMeta::getMetazoneID(tzID, date, mzID); michael@0: return mzID; michael@0: } michael@0: michael@0: UnicodeString& michael@0: TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { michael@0: ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID); michael@0: return tzID; michael@0: } michael@0: michael@0: UnicodeString& michael@0: TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID, michael@0: UTimeZoneNameType type, michael@0: UnicodeString& name) const { michael@0: name.setToBogus(); // cleanup result. michael@0: if (mzID.isEmpty()) { michael@0: return name; michael@0: } michael@0: michael@0: ZNames *znames = NULL; michael@0: TimeZoneNamesImpl *nonConstThis = const_cast(this); michael@0: michael@0: umtx_lock(&gLock); michael@0: { michael@0: znames = nonConstThis->loadMetaZoneNames(mzID); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: if (znames != NULL) { michael@0: const UChar* s = znames->getName(type); michael@0: if (s != NULL) { michael@0: name.setTo(TRUE, s, -1); michael@0: } michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: UnicodeString& michael@0: TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { michael@0: name.setToBogus(); // cleanup result. michael@0: if (tzID.isEmpty()) { michael@0: return name; michael@0: } michael@0: michael@0: TZNames *tznames = NULL; michael@0: TimeZoneNamesImpl *nonConstThis = const_cast(this); michael@0: michael@0: umtx_lock(&gLock); michael@0: { michael@0: tznames = nonConstThis->loadTimeZoneNames(tzID); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: if (tznames != NULL) { michael@0: const UChar *s = tznames->getName(type); michael@0: if (s != NULL) { michael@0: name.setTo(TRUE, s, -1); michael@0: } michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: UnicodeString& michael@0: TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { michael@0: name.setToBogus(); // cleanup result. michael@0: const UChar* locName = NULL; michael@0: TZNames *tznames = NULL; michael@0: TimeZoneNamesImpl *nonConstThis = const_cast(this); michael@0: michael@0: umtx_lock(&gLock); michael@0: { michael@0: tznames = nonConstThis->loadTimeZoneNames(tzID); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: if (tznames != NULL) { michael@0: locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION); michael@0: } michael@0: if (locName != NULL) { michael@0: name.setTo(TRUE, locName, -1); michael@0: } michael@0: michael@0: return name; michael@0: } michael@0: michael@0: michael@0: // Merge the MZ_PREFIX and mzId michael@0: static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) { michael@0: if (mzID.isEmpty()) { michael@0: result[0] = '\0'; michael@0: return; michael@0: } michael@0: michael@0: char mzIdChar[ZID_KEY_MAX + 1]; michael@0: int32_t keyLen; michael@0: int32_t prefixLen = uprv_strlen(gMZPrefix); michael@0: keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV); michael@0: uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen); michael@0: uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen); michael@0: result[keyLen + prefixLen] = '\0'; michael@0: } michael@0: michael@0: /* michael@0: * This method updates the cache and must be called with a lock michael@0: */ michael@0: ZNames* michael@0: TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) { michael@0: if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) { michael@0: return NULL; michael@0: } michael@0: michael@0: ZNames *znames = NULL; michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: UChar mzIDKey[ZID_KEY_MAX + 1]; michael@0: mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status); michael@0: U_ASSERT(status == U_ZERO_ERROR); // already checked length above michael@0: mzIDKey[mzID.length()] = 0; michael@0: michael@0: void *cacheVal = uhash_get(fMZNamesMap, mzIDKey); michael@0: if (cacheVal == NULL) { michael@0: char key[ZID_KEY_MAX + 1]; michael@0: mergeTimeZoneKey(mzID, key); michael@0: znames = ZNames::createInstance(fZoneStrings, key); michael@0: michael@0: if (znames == NULL) { michael@0: cacheVal = (void *)EMPTY; michael@0: } else { michael@0: cacheVal = znames; michael@0: } michael@0: // Use the persistent ID as the resource key, so we can michael@0: // avoid duplications. michael@0: const UChar* newKey = ZoneMeta::findMetaZoneID(mzID); michael@0: if (newKey != NULL) { michael@0: uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status); michael@0: if (U_FAILURE(status)) { michael@0: if (znames != NULL) { michael@0: delete znames; michael@0: } michael@0: } else if (znames != NULL) { michael@0: // put the name info into the trie michael@0: for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) { michael@0: const UChar* name = znames->getName(ALL_NAME_TYPES[i]); michael@0: if (name != NULL) { michael@0: ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo)); michael@0: if (nameinfo != NULL) { michael@0: nameinfo->type = ALL_NAME_TYPES[i]; michael@0: nameinfo->tzID = NULL; michael@0: nameinfo->mzID = newKey; michael@0: fNamesTrie.put(name, nameinfo, status); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: } else { michael@0: // Should never happen with a valid input michael@0: if (znames != NULL) { michael@0: // It's not possible that we get a valid ZNames with unknown ID. michael@0: // But just in case.. michael@0: delete znames; michael@0: znames = NULL; michael@0: } michael@0: } michael@0: } else if (cacheVal != EMPTY) { michael@0: znames = (ZNames *)cacheVal; michael@0: } michael@0: michael@0: return znames; michael@0: } michael@0: michael@0: /* michael@0: * This method updates the cache and must be called with a lock michael@0: */ michael@0: TZNames* michael@0: TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) { michael@0: if (tzID.length() > ZID_KEY_MAX) { michael@0: return NULL; michael@0: } michael@0: michael@0: TZNames *tznames = NULL; michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: UChar tzIDKey[ZID_KEY_MAX + 1]; michael@0: int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status); michael@0: U_ASSERT(status == U_ZERO_ERROR); // already checked length above michael@0: tzIDKey[tzIDKeyLen] = 0; michael@0: michael@0: void *cacheVal = uhash_get(fTZNamesMap, tzIDKey); michael@0: if (cacheVal == NULL) { michael@0: char key[ZID_KEY_MAX + 1]; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: // Replace "/" with ":". michael@0: UnicodeString uKey(tzID); michael@0: for (int32_t i = 0; i < uKey.length(); i++) { michael@0: if (uKey.charAt(i) == (UChar)0x2F) { michael@0: uKey.setCharAt(i, (UChar)0x3A); michael@0: } michael@0: } michael@0: uKey.extract(0, uKey.length(), key, sizeof(key), US_INV); michael@0: tznames = TZNames::createInstance(fZoneStrings, key, tzID); michael@0: michael@0: if (tznames == NULL) { michael@0: cacheVal = (void *)EMPTY; michael@0: } else { michael@0: cacheVal = tznames; michael@0: } michael@0: // Use the persistent ID as the resource key, so we can michael@0: // avoid duplications. michael@0: const UChar* newKey = ZoneMeta::findTimeZoneID(tzID); michael@0: if (newKey != NULL) { michael@0: uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status); michael@0: if (U_FAILURE(status)) { michael@0: if (tznames != NULL) { michael@0: delete tznames; michael@0: } michael@0: } else if (tznames != NULL) { michael@0: // put the name info into the trie michael@0: for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) { michael@0: const UChar* name = tznames->getName(ALL_NAME_TYPES[i]); michael@0: if (name != NULL) { michael@0: ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo)); michael@0: if (nameinfo != NULL) { michael@0: nameinfo->type = ALL_NAME_TYPES[i]; michael@0: nameinfo->tzID = newKey; michael@0: nameinfo->mzID = NULL; michael@0: fNamesTrie.put(name, nameinfo, status); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: // Should never happen with a valid input michael@0: if (tznames != NULL) { michael@0: // It's not possible that we get a valid TZNames with unknown ID. michael@0: // But just in case.. michael@0: delete tznames; michael@0: tznames = NULL; michael@0: } michael@0: } michael@0: } else if (cacheVal != EMPTY) { michael@0: tznames = (TZNames *)cacheVal; michael@0: } michael@0: michael@0: return tznames; michael@0: } michael@0: michael@0: TimeZoneNames::MatchInfoCollection* michael@0: TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { michael@0: ZNameSearchHandler handler(types); michael@0: michael@0: TimeZoneNamesImpl *nonConstThis = const_cast(this); michael@0: michael@0: umtx_lock(&gLock); michael@0: { michael@0: fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: michael@0: int32_t maxLen = 0; michael@0: TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen); michael@0: if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) { michael@0: // perfect match michael@0: return matches; michael@0: } michael@0: michael@0: delete matches; michael@0: michael@0: // All names are not yet loaded into the trie michael@0: umtx_lock(&gLock); michael@0: { michael@0: if (!fNamesTrieFullyLoaded) { michael@0: const UnicodeString *id; michael@0: michael@0: // load strings for all zones michael@0: StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); michael@0: if (U_SUCCESS(status)) { michael@0: while ((id = tzIDs->snext(status))) { michael@0: if (U_FAILURE(status)) { michael@0: break; michael@0: } michael@0: // loadStrings also load related metazone strings michael@0: nonConstThis->loadStrings(*id); michael@0: } michael@0: } michael@0: if (tzIDs != NULL) { michael@0: delete tzIDs; michael@0: } michael@0: if (U_SUCCESS(status)) { michael@0: nonConstThis->fNamesTrieFullyLoaded = TRUE; michael@0: } michael@0: } michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: michael@0: umtx_lock(&gLock); michael@0: { michael@0: // now try it again michael@0: fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: michael@0: return handler.getMatches(maxLen); michael@0: } michael@0: michael@0: static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/" michael@0: static const int32_t gEtcPrefixLen = 4; michael@0: static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/ michael@0: static const int32_t gSystemVPrefixLen = 8; michael@0: static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8" michael@0: static const int32_t gRiyadh8Len = 7; michael@0: michael@0: UnicodeString& U_EXPORT2 michael@0: TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) { michael@0: if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen) michael@0: || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) { michael@0: name.setToBogus(); michael@0: return name; michael@0: } michael@0: michael@0: int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */); michael@0: if (sep > 0 && sep + 1 < tzID.length()) { michael@0: name.setTo(tzID, sep + 1); michael@0: name.findAndReplace(UnicodeString((UChar)0x5f /* _ */), michael@0: UnicodeString((UChar)0x20 /* space */)); michael@0: } else { michael@0: name.setToBogus(); michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof