1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/tznames_impl.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1379 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 2011-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +* 1.10 +* File TZNAMES_IMPL.CPP 1.11 +* 1.12 +******************************************************************************* 1.13 +*/ 1.14 + 1.15 +#include "unicode/utypes.h" 1.16 + 1.17 +#if !UCONFIG_NO_FORMATTING 1.18 + 1.19 +#include "unicode/ustring.h" 1.20 +#include "unicode/timezone.h" 1.21 + 1.22 +#include "tznames_impl.h" 1.23 +#include "cmemory.h" 1.24 +#include "cstring.h" 1.25 +#include "uassert.h" 1.26 +#include "mutex.h" 1.27 +#include "uresimp.h" 1.28 +#include "ureslocs.h" 1.29 +#include "zonemeta.h" 1.30 +#include "ucln_in.h" 1.31 +#include "uvector.h" 1.32 +#include "olsontz.h" 1.33 + 1.34 + 1.35 +U_NAMESPACE_BEGIN 1.36 + 1.37 +#define ZID_KEY_MAX 128 1.38 +#define MZ_PREFIX_LEN 5 1.39 + 1.40 +static const char gZoneStrings[] = "zoneStrings"; 1.41 +static const char gMZPrefix[] = "meta:"; 1.42 + 1.43 +static const char* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"}; 1.44 +static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]); 1.45 + 1.46 +static const char gEcTag[] = "ec"; 1.47 + 1.48 +static const char EMPTY[] = "<empty>"; // place holder for empty ZNames/TZNames 1.49 + 1.50 +static const UTimeZoneNameType ALL_NAME_TYPES[] = { 1.51 + UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, 1.52 + UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, 1.53 + UTZNM_EXEMPLAR_LOCATION, 1.54 + UTZNM_UNKNOWN // unknown as the last one 1.55 +}; 1.56 + 1.57 +#define DEFAULT_CHARACTERNODE_CAPACITY 1 1.58 + 1.59 +// --------------------------------------------------- 1.60 +// CharacterNode class implementation 1.61 +// --------------------------------------------------- 1.62 +void CharacterNode::clear() { 1.63 + uprv_memset(this, 0, sizeof(*this)); 1.64 +} 1.65 + 1.66 +void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) { 1.67 + if (fValues == NULL) { 1.68 + // Do nothing. 1.69 + } else if (!fHasValuesVector) { 1.70 + if (valueDeleter) { 1.71 + valueDeleter(fValues); 1.72 + } 1.73 + } else { 1.74 + delete (UVector *)fValues; 1.75 + } 1.76 +} 1.77 + 1.78 +void 1.79 +CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) { 1.80 + if (U_FAILURE(status)) { 1.81 + if (valueDeleter) { 1.82 + valueDeleter(value); 1.83 + } 1.84 + return; 1.85 + } 1.86 + if (fValues == NULL) { 1.87 + fValues = value; 1.88 + } else { 1.89 + // At least one value already. 1.90 + if (!fHasValuesVector) { 1.91 + // There is only one value so far, and not in a vector yet. 1.92 + // Create a vector and add the old value. 1.93 + UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); 1.94 + if (U_FAILURE(status)) { 1.95 + if (valueDeleter) { 1.96 + valueDeleter(value); 1.97 + } 1.98 + return; 1.99 + } 1.100 + values->addElement(fValues, status); 1.101 + fValues = values; 1.102 + fHasValuesVector = TRUE; 1.103 + } 1.104 + // Add the new value. 1.105 + ((UVector *)fValues)->addElement(value, status); 1.106 + } 1.107 +} 1.108 + 1.109 +// --------------------------------------------------- 1.110 +// TextTrieMapSearchResultHandler class implementation 1.111 +// --------------------------------------------------- 1.112 +TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ 1.113 +} 1.114 + 1.115 +// --------------------------------------------------- 1.116 +// TextTrieMap class implementation 1.117 +// --------------------------------------------------- 1.118 +TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter) 1.119 +: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0), 1.120 + fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) { 1.121 +} 1.122 + 1.123 +TextTrieMap::~TextTrieMap() { 1.124 + int32_t index; 1.125 + for (index = 0; index < fNodesCount; ++index) { 1.126 + fNodes[index].deleteValues(fValueDeleter); 1.127 + } 1.128 + uprv_free(fNodes); 1.129 + if (fLazyContents != NULL) { 1.130 + for (int32_t i=0; i<fLazyContents->size(); i+=2) { 1.131 + if (fValueDeleter) { 1.132 + fValueDeleter(fLazyContents->elementAt(i+1)); 1.133 + } 1.134 + } 1.135 + delete fLazyContents; 1.136 + } 1.137 +} 1.138 + 1.139 +int32_t TextTrieMap::isEmpty() const { 1.140 + // Use a separate field for fIsEmpty because it will remain unchanged once the 1.141 + // Trie is built, while fNodes and fLazyContents change with the lazy init 1.142 + // of the nodes structure. Trying to test the changing fields has 1.143 + // thread safety complications. 1.144 + return fIsEmpty; 1.145 +} 1.146 + 1.147 + 1.148 +// We defer actually building the TextTrieMap node structure until the first time a 1.149 +// search is performed. put() simply saves the parameters in case we do 1.150 +// eventually need to build it. 1.151 +// 1.152 +void 1.153 +TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) { 1.154 + const UChar *s = sp.get(key, status); 1.155 + put(s, value, status); 1.156 +} 1.157 + 1.158 +// This method is for designed for a persistent key, such as string key stored in 1.159 +// resource bundle. 1.160 +void 1.161 +TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { 1.162 + fIsEmpty = FALSE; 1.163 + if (fLazyContents == NULL) { 1.164 + fLazyContents = new UVector(status); 1.165 + if (fLazyContents == NULL) { 1.166 + status = U_MEMORY_ALLOCATION_ERROR; 1.167 + } 1.168 + } 1.169 + if (U_FAILURE(status)) { 1.170 + return; 1.171 + } 1.172 + U_ASSERT(fLazyContents != NULL); 1.173 + UChar *s = const_cast<UChar *>(key); 1.174 + fLazyContents->addElement(s, status); 1.175 + fLazyContents->addElement(value, status); 1.176 +} 1.177 + 1.178 +void 1.179 +TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) { 1.180 + if (fNodes == NULL) { 1.181 + fNodesCapacity = 512; 1.182 + fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); 1.183 + fNodes[0].clear(); // Init root node. 1.184 + fNodesCount = 1; 1.185 + } 1.186 + 1.187 + UnicodeString foldedKey; 1.188 + const UChar *keyBuffer; 1.189 + int32_t keyLength; 1.190 + if (fIgnoreCase) { 1.191 + // Ok to use fastCopyFrom() because we discard the copy when we return. 1.192 + foldedKey.fastCopyFrom(key).foldCase(); 1.193 + keyBuffer = foldedKey.getBuffer(); 1.194 + keyLength = foldedKey.length(); 1.195 + } else { 1.196 + keyBuffer = key.getBuffer(); 1.197 + keyLength = key.length(); 1.198 + } 1.199 + 1.200 + CharacterNode *node = fNodes; 1.201 + int32_t index; 1.202 + for (index = 0; index < keyLength; ++index) { 1.203 + node = addChildNode(node, keyBuffer[index], status); 1.204 + } 1.205 + node->addValue(value, fValueDeleter, status); 1.206 +} 1.207 + 1.208 +UBool 1.209 +TextTrieMap::growNodes() { 1.210 + if (fNodesCapacity == 0xffff) { 1.211 + return FALSE; // We use 16-bit node indexes. 1.212 + } 1.213 + int32_t newCapacity = fNodesCapacity + 1000; 1.214 + if (newCapacity > 0xffff) { 1.215 + newCapacity = 0xffff; 1.216 + } 1.217 + CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode)); 1.218 + if (newNodes == NULL) { 1.219 + return FALSE; 1.220 + } 1.221 + uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); 1.222 + uprv_free(fNodes); 1.223 + fNodes = newNodes; 1.224 + fNodesCapacity = newCapacity; 1.225 + return TRUE; 1.226 +} 1.227 + 1.228 +CharacterNode* 1.229 +TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) { 1.230 + if (U_FAILURE(status)) { 1.231 + return NULL; 1.232 + } 1.233 + // Linear search of the sorted list of children. 1.234 + uint16_t prevIndex = 0; 1.235 + uint16_t nodeIndex = parent->fFirstChild; 1.236 + while (nodeIndex > 0) { 1.237 + CharacterNode *current = fNodes + nodeIndex; 1.238 + UChar childCharacter = current->fCharacter; 1.239 + if (childCharacter == c) { 1.240 + return current; 1.241 + } else if (childCharacter > c) { 1.242 + break; 1.243 + } 1.244 + prevIndex = nodeIndex; 1.245 + nodeIndex = current->fNextSibling; 1.246 + } 1.247 + 1.248 + // Ensure capacity. Grow fNodes[] if needed. 1.249 + if (fNodesCount == fNodesCapacity) { 1.250 + int32_t parentIndex = (int32_t)(parent - fNodes); 1.251 + if (!growNodes()) { 1.252 + status = U_MEMORY_ALLOCATION_ERROR; 1.253 + return NULL; 1.254 + } 1.255 + parent = fNodes + parentIndex; 1.256 + } 1.257 + 1.258 + // Insert a new child node with c in sorted order. 1.259 + CharacterNode *node = fNodes + fNodesCount; 1.260 + node->clear(); 1.261 + node->fCharacter = c; 1.262 + node->fNextSibling = nodeIndex; 1.263 + if (prevIndex == 0) { 1.264 + parent->fFirstChild = (uint16_t)fNodesCount; 1.265 + } else { 1.266 + fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount; 1.267 + } 1.268 + ++fNodesCount; 1.269 + return node; 1.270 +} 1.271 + 1.272 +CharacterNode* 1.273 +TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const { 1.274 + // Linear search of the sorted list of children. 1.275 + uint16_t nodeIndex = parent->fFirstChild; 1.276 + while (nodeIndex > 0) { 1.277 + CharacterNode *current = fNodes + nodeIndex; 1.278 + UChar childCharacter = current->fCharacter; 1.279 + if (childCharacter == c) { 1.280 + return current; 1.281 + } else if (childCharacter > c) { 1.282 + break; 1.283 + } 1.284 + nodeIndex = current->fNextSibling; 1.285 + } 1.286 + return NULL; 1.287 +} 1.288 + 1.289 +// Mutex for protecting the lazy creation of the Trie node structure on the first call to search(). 1.290 +static UMutex TextTrieMutex = U_MUTEX_INITIALIZER; 1.291 + 1.292 +// buildTrie() - The Trie node structure is needed. Create it from the data that was 1.293 +// saved at the time the ZoneStringFormatter was created. The Trie is only 1.294 +// needed for parsing operations, which are less common than formatting, 1.295 +// and the Trie is big, which is why its creation is deferred until first use. 1.296 +void TextTrieMap::buildTrie(UErrorCode &status) { 1.297 + if (fLazyContents != NULL) { 1.298 + for (int32_t i=0; i<fLazyContents->size(); i+=2) { 1.299 + const UChar *key = (UChar *)fLazyContents->elementAt(i); 1.300 + void *val = fLazyContents->elementAt(i+1); 1.301 + UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor. 1.302 + putImpl(keyString, val, status); 1.303 + } 1.304 + delete fLazyContents; 1.305 + fLazyContents = NULL; 1.306 + } 1.307 +} 1.308 + 1.309 +void 1.310 +TextTrieMap::search(const UnicodeString &text, int32_t start, 1.311 + TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 1.312 + { 1.313 + // TODO: if locking the mutex for each check proves to be a performance problem, 1.314 + // add a flag of type atomic_int32_t to class TextTrieMap, and use only 1.315 + // the ICU atomic safe functions for assigning and testing. 1.316 + // Don't test the pointer fLazyContents. 1.317 + // Don't do unless it's really required. 1.318 + Mutex lock(&TextTrieMutex); 1.319 + if (fLazyContents != NULL) { 1.320 + TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this); 1.321 + nonConstThis->buildTrie(status); 1.322 + } 1.323 + } 1.324 + if (fNodes == NULL) { 1.325 + return; 1.326 + } 1.327 + search(fNodes, text, start, start, handler, status); 1.328 +} 1.329 + 1.330 +void 1.331 +TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, 1.332 + int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 1.333 + if (U_FAILURE(status)) { 1.334 + return; 1.335 + } 1.336 + if (node->hasValues()) { 1.337 + if (!handler->handleMatch(index - start, node, status)) { 1.338 + return; 1.339 + } 1.340 + if (U_FAILURE(status)) { 1.341 + return; 1.342 + } 1.343 + } 1.344 + UChar32 c = text.char32At(index); 1.345 + if (fIgnoreCase) { 1.346 + // size of character may grow after fold operation 1.347 + UnicodeString tmp(c); 1.348 + tmp.foldCase(); 1.349 + int32_t tmpidx = 0; 1.350 + while (tmpidx < tmp.length()) { 1.351 + c = tmp.char32At(tmpidx); 1.352 + node = getChildNode(node, c); 1.353 + if (node == NULL) { 1.354 + break; 1.355 + } 1.356 + tmpidx = tmp.moveIndex32(tmpidx, 1); 1.357 + } 1.358 + } else { 1.359 + node = getChildNode(node, c); 1.360 + } 1.361 + if (node != NULL) { 1.362 + search(node, text, start, index+1, handler, status); 1.363 + } 1.364 +} 1.365 + 1.366 +// --------------------------------------------------- 1.367 +// ZNStringPool class implementation 1.368 +// --------------------------------------------------- 1.369 +static const int32_t POOL_CHUNK_SIZE = 2000; 1.370 +struct ZNStringPoolChunk: public UMemory { 1.371 + ZNStringPoolChunk *fNext; // Ptr to next pool chunk 1.372 + int32_t fLimit; // Index to start of unused area at end of fStrings 1.373 + UChar fStrings[POOL_CHUNK_SIZE]; // Strings array 1.374 + ZNStringPoolChunk(); 1.375 +}; 1.376 + 1.377 +ZNStringPoolChunk::ZNStringPoolChunk() { 1.378 + fNext = NULL; 1.379 + fLimit = 0; 1.380 +} 1.381 + 1.382 +ZNStringPool::ZNStringPool(UErrorCode &status) { 1.383 + fChunks = NULL; 1.384 + fHash = NULL; 1.385 + if (U_FAILURE(status)) { 1.386 + return; 1.387 + } 1.388 + fChunks = new ZNStringPoolChunk; 1.389 + if (fChunks == NULL) { 1.390 + status = U_MEMORY_ALLOCATION_ERROR; 1.391 + return; 1.392 + } 1.393 + 1.394 + fHash = uhash_open(uhash_hashUChars /* keyHash */, 1.395 + uhash_compareUChars /* keyComp */, 1.396 + uhash_compareUChars /* valueComp */, 1.397 + &status); 1.398 + if (U_FAILURE(status)) { 1.399 + return; 1.400 + } 1.401 +} 1.402 + 1.403 +ZNStringPool::~ZNStringPool() { 1.404 + if (fHash != NULL) { 1.405 + uhash_close(fHash); 1.406 + fHash = NULL; 1.407 + } 1.408 + 1.409 + while (fChunks != NULL) { 1.410 + ZNStringPoolChunk *nextChunk = fChunks->fNext; 1.411 + delete fChunks; 1.412 + fChunks = nextChunk; 1.413 + } 1.414 +} 1.415 + 1.416 +static const UChar EmptyString = 0; 1.417 + 1.418 +const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) { 1.419 + const UChar *pooledString; 1.420 + if (U_FAILURE(status)) { 1.421 + return &EmptyString; 1.422 + } 1.423 + 1.424 + pooledString = static_cast<UChar *>(uhash_get(fHash, s)); 1.425 + if (pooledString != NULL) { 1.426 + return pooledString; 1.427 + } 1.428 + 1.429 + int32_t length = u_strlen(s); 1.430 + int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit; 1.431 + if (remainingLength <= length) { 1.432 + U_ASSERT(length < POOL_CHUNK_SIZE); 1.433 + if (length >= POOL_CHUNK_SIZE) { 1.434 + status = U_INTERNAL_PROGRAM_ERROR; 1.435 + return &EmptyString; 1.436 + } 1.437 + ZNStringPoolChunk *oldChunk = fChunks; 1.438 + fChunks = new ZNStringPoolChunk; 1.439 + if (fChunks == NULL) { 1.440 + status = U_MEMORY_ALLOCATION_ERROR; 1.441 + return &EmptyString; 1.442 + } 1.443 + fChunks->fNext = oldChunk; 1.444 + } 1.445 + 1.446 + UChar *destString = &fChunks->fStrings[fChunks->fLimit]; 1.447 + u_strcpy(destString, s); 1.448 + fChunks->fLimit += (length + 1); 1.449 + uhash_put(fHash, destString, destString, &status); 1.450 + return destString; 1.451 +} 1.452 + 1.453 + 1.454 +// 1.455 +// ZNStringPool::adopt() Put a string into the hash, but do not copy the string data 1.456 +// into the pool's storage. Used for strings from resource bundles, 1.457 +// which will perisist for the life of the zone string formatter, and 1.458 +// therefore can be used directly without copying. 1.459 +const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) { 1.460 + const UChar *pooledString; 1.461 + if (U_FAILURE(status)) { 1.462 + return &EmptyString; 1.463 + } 1.464 + if (s != NULL) { 1.465 + pooledString = static_cast<UChar *>(uhash_get(fHash, s)); 1.466 + if (pooledString == NULL) { 1.467 + UChar *ncs = const_cast<UChar *>(s); 1.468 + uhash_put(fHash, ncs, ncs, &status); 1.469 + } 1.470 + } 1.471 + return s; 1.472 +} 1.473 + 1.474 + 1.475 +const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) { 1.476 + UnicodeString &nonConstStr = const_cast<UnicodeString &>(s); 1.477 + return this->get(nonConstStr.getTerminatedBuffer(), status); 1.478 +} 1.479 + 1.480 +/* 1.481 + * freeze(). Close the hash table that maps to the pooled strings. 1.482 + * After freezing, the pool can not be searched or added to, 1.483 + * but all existing references to pooled strings remain valid. 1.484 + * 1.485 + * The main purpose is to recover the storage used for the hash. 1.486 + */ 1.487 +void ZNStringPool::freeze() { 1.488 + uhash_close(fHash); 1.489 + fHash = NULL; 1.490 +} 1.491 + 1.492 + 1.493 +// --------------------------------------------------- 1.494 +// ZNames - names common for time zone and meta zone 1.495 +// --------------------------------------------------- 1.496 +class ZNames : public UMemory { 1.497 +public: 1.498 + virtual ~ZNames(); 1.499 + 1.500 + static ZNames* createInstance(UResourceBundle* rb, const char* key); 1.501 + virtual const UChar* getName(UTimeZoneNameType type); 1.502 + 1.503 +protected: 1.504 + ZNames(const UChar** names); 1.505 + static const UChar** loadData(UResourceBundle* rb, const char* key); 1.506 + 1.507 +private: 1.508 + const UChar** fNames; 1.509 +}; 1.510 + 1.511 +ZNames::ZNames(const UChar** names) 1.512 +: fNames(names) { 1.513 +} 1.514 + 1.515 +ZNames::~ZNames() { 1.516 + if (fNames != NULL) { 1.517 + uprv_free(fNames); 1.518 + } 1.519 +} 1.520 + 1.521 +ZNames* 1.522 +ZNames::createInstance(UResourceBundle* rb, const char* key) { 1.523 + const UChar** names = loadData(rb, key); 1.524 + if (names == NULL) { 1.525 + // No names data available 1.526 + return NULL; 1.527 + } 1.528 + return new ZNames(names); 1.529 +} 1.530 + 1.531 +const UChar* 1.532 +ZNames::getName(UTimeZoneNameType type) { 1.533 + if (fNames == NULL) { 1.534 + return NULL; 1.535 + } 1.536 + const UChar *name = NULL; 1.537 + switch(type) { 1.538 + case UTZNM_LONG_GENERIC: 1.539 + name = fNames[0]; 1.540 + break; 1.541 + case UTZNM_LONG_STANDARD: 1.542 + name = fNames[1]; 1.543 + break; 1.544 + case UTZNM_LONG_DAYLIGHT: 1.545 + name = fNames[2]; 1.546 + break; 1.547 + case UTZNM_SHORT_GENERIC: 1.548 + name = fNames[3]; 1.549 + break; 1.550 + case UTZNM_SHORT_STANDARD: 1.551 + name = fNames[4]; 1.552 + break; 1.553 + case UTZNM_SHORT_DAYLIGHT: 1.554 + name = fNames[5]; 1.555 + break; 1.556 + case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass 1.557 + default: 1.558 + name = NULL; 1.559 + } 1.560 + return name; 1.561 +} 1.562 + 1.563 +const UChar** 1.564 +ZNames::loadData(UResourceBundle* rb, const char* key) { 1.565 + if (rb == NULL || key == NULL || *key == 0) { 1.566 + return NULL; 1.567 + } 1.568 + 1.569 + UErrorCode status = U_ZERO_ERROR; 1.570 + const UChar **names = NULL; 1.571 + 1.572 + UResourceBundle* rbTable = NULL; 1.573 + rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status); 1.574 + if (U_SUCCESS(status)) { 1.575 + names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE); 1.576 + if (names != NULL) { 1.577 + UBool isEmpty = TRUE; 1.578 + for (int32_t i = 0; i < KEYS_SIZE; i++) { 1.579 + status = U_ZERO_ERROR; 1.580 + int32_t len = 0; 1.581 + const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status); 1.582 + if (U_FAILURE(status) || len == 0) { 1.583 + names[i] = NULL; 1.584 + } else { 1.585 + names[i] = value; 1.586 + isEmpty = FALSE; 1.587 + } 1.588 + } 1.589 + if (isEmpty) { 1.590 + // No need to keep the names array 1.591 + uprv_free(names); 1.592 + names = NULL; 1.593 + } 1.594 + } 1.595 + } 1.596 + ures_close(rbTable); 1.597 + return names; 1.598 +} 1.599 + 1.600 +// --------------------------------------------------- 1.601 +// TZNames - names for a time zone 1.602 +// --------------------------------------------------- 1.603 +class TZNames : public ZNames { 1.604 +public: 1.605 + virtual ~TZNames(); 1.606 + 1.607 + static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID); 1.608 + virtual const UChar* getName(UTimeZoneNameType type); 1.609 + 1.610 +private: 1.611 + TZNames(const UChar** names); 1.612 + const UChar* fLocationName; 1.613 + UChar* fLocationNameOwned; 1.614 +}; 1.615 + 1.616 +TZNames::TZNames(const UChar** names) 1.617 +: ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) { 1.618 +} 1.619 + 1.620 +TZNames::~TZNames() { 1.621 + if (fLocationNameOwned) { 1.622 + uprv_free(fLocationNameOwned); 1.623 + } 1.624 +} 1.625 + 1.626 +const UChar* 1.627 +TZNames::getName(UTimeZoneNameType type) { 1.628 + if (type == UTZNM_EXEMPLAR_LOCATION) { 1.629 + return fLocationName; 1.630 + } 1.631 + return ZNames::getName(type); 1.632 +} 1.633 + 1.634 +TZNames* 1.635 +TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) { 1.636 + if (rb == NULL || key == NULL || *key == 0) { 1.637 + return NULL; 1.638 + } 1.639 + 1.640 + const UChar** names = loadData(rb, key); 1.641 + const UChar* locationName = NULL; 1.642 + UChar* locationNameOwned = NULL; 1.643 + 1.644 + UErrorCode status = U_ZERO_ERROR; 1.645 + int32_t len = 0; 1.646 + 1.647 + UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status); 1.648 + locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status); 1.649 + // ignore missing resource here 1.650 + status = U_ZERO_ERROR; 1.651 + 1.652 + ures_close(table); 1.653 + 1.654 + if (locationName == NULL) { 1.655 + UnicodeString tmpName; 1.656 + int32_t tmpNameLen = 0; 1.657 + TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName); 1.658 + tmpNameLen = tmpName.length(); 1.659 + 1.660 + if (tmpNameLen > 0) { 1.661 + locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1)); 1.662 + if (locationNameOwned) { 1.663 + tmpName.extract(locationNameOwned, tmpNameLen + 1, status); 1.664 + locationName = locationNameOwned; 1.665 + } 1.666 + } 1.667 + } 1.668 + 1.669 + TZNames* tznames = NULL; 1.670 + if (locationName != NULL || names != NULL) { 1.671 + tznames = new TZNames(names); 1.672 + if (tznames == NULL) { 1.673 + if (locationNameOwned) { 1.674 + uprv_free(locationNameOwned); 1.675 + } 1.676 + } 1.677 + tznames->fLocationName = locationName; 1.678 + tznames->fLocationNameOwned = locationNameOwned; 1.679 + } 1.680 + 1.681 + return tznames; 1.682 +} 1.683 + 1.684 +// --------------------------------------------------- 1.685 +// The meta zone ID enumeration class 1.686 +// --------------------------------------------------- 1.687 +class MetaZoneIDsEnumeration : public StringEnumeration { 1.688 +public: 1.689 + MetaZoneIDsEnumeration(); 1.690 + MetaZoneIDsEnumeration(const UVector& mzIDs); 1.691 + MetaZoneIDsEnumeration(UVector* mzIDs); 1.692 + virtual ~MetaZoneIDsEnumeration(); 1.693 + static UClassID U_EXPORT2 getStaticClassID(void); 1.694 + virtual UClassID getDynamicClassID(void) const; 1.695 + virtual const UnicodeString* snext(UErrorCode& status); 1.696 + virtual void reset(UErrorCode& status); 1.697 + virtual int32_t count(UErrorCode& status) const; 1.698 +private: 1.699 + int32_t fLen; 1.700 + int32_t fPos; 1.701 + const UVector* fMetaZoneIDs; 1.702 + UVector *fLocalVector; 1.703 +}; 1.704 + 1.705 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration) 1.706 + 1.707 +MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() 1.708 +: fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) { 1.709 +} 1.710 + 1.711 +MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) 1.712 +: fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) { 1.713 + fLen = fMetaZoneIDs->size(); 1.714 +} 1.715 + 1.716 +MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs) 1.717 +: fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) { 1.718 + if (fMetaZoneIDs) { 1.719 + fLen = fMetaZoneIDs->size(); 1.720 + } 1.721 +} 1.722 + 1.723 +const UnicodeString* 1.724 +MetaZoneIDsEnumeration::snext(UErrorCode& status) { 1.725 + if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) { 1.726 + unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1); 1.727 + return &unistr; 1.728 + } 1.729 + return NULL; 1.730 +} 1.731 + 1.732 +void 1.733 +MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) { 1.734 + fPos = 0; 1.735 +} 1.736 + 1.737 +int32_t 1.738 +MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const { 1.739 + return fLen; 1.740 +} 1.741 + 1.742 +MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() { 1.743 + if (fLocalVector) { 1.744 + delete fLocalVector; 1.745 + } 1.746 +} 1.747 + 1.748 +U_CDECL_BEGIN 1.749 +/** 1.750 + * ZNameInfo stores zone name information in the trie 1.751 + */ 1.752 +typedef struct ZNameInfo { 1.753 + UTimeZoneNameType type; 1.754 + const UChar* tzID; 1.755 + const UChar* mzID; 1.756 +} ZNameInfo; 1.757 + 1.758 +/** 1.759 + * ZMatchInfo stores zone name match information used by find method 1.760 + */ 1.761 +typedef struct ZMatchInfo { 1.762 + const ZNameInfo* znameInfo; 1.763 + int32_t matchLength; 1.764 +} ZMatchInfo; 1.765 +U_CDECL_END 1.766 + 1.767 + 1.768 +// --------------------------------------------------- 1.769 +// ZNameSearchHandler 1.770 +// --------------------------------------------------- 1.771 +class ZNameSearchHandler : public TextTrieMapSearchResultHandler { 1.772 +public: 1.773 + ZNameSearchHandler(uint32_t types); 1.774 + virtual ~ZNameSearchHandler(); 1.775 + 1.776 + UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); 1.777 + TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen); 1.778 + 1.779 +private: 1.780 + uint32_t fTypes; 1.781 + int32_t fMaxMatchLen; 1.782 + TimeZoneNames::MatchInfoCollection* fResults; 1.783 +}; 1.784 + 1.785 +ZNameSearchHandler::ZNameSearchHandler(uint32_t types) 1.786 +: fTypes(types), fMaxMatchLen(0), fResults(NULL) { 1.787 +} 1.788 + 1.789 +ZNameSearchHandler::~ZNameSearchHandler() { 1.790 + if (fResults != NULL) { 1.791 + delete fResults; 1.792 + } 1.793 +} 1.794 + 1.795 +UBool 1.796 +ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 1.797 + if (U_FAILURE(status)) { 1.798 + return FALSE; 1.799 + } 1.800 + if (node->hasValues()) { 1.801 + int32_t valuesCount = node->countValues(); 1.802 + for (int32_t i = 0; i < valuesCount; i++) { 1.803 + ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); 1.804 + if (nameinfo == NULL) { 1.805 + break; 1.806 + } 1.807 + if ((nameinfo->type & fTypes) != 0) { 1.808 + // matches a requested type 1.809 + if (fResults == NULL) { 1.810 + fResults = new TimeZoneNames::MatchInfoCollection(); 1.811 + if (fResults == NULL) { 1.812 + status = U_MEMORY_ALLOCATION_ERROR; 1.813 + } 1.814 + } 1.815 + if (U_SUCCESS(status)) { 1.816 + U_ASSERT(fResults != NULL); 1.817 + if (nameinfo->tzID) { 1.818 + fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status); 1.819 + } else { 1.820 + U_ASSERT(nameinfo->mzID); 1.821 + fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status); 1.822 + } 1.823 + if (U_SUCCESS(status) && matchLength > fMaxMatchLen) { 1.824 + fMaxMatchLen = matchLength; 1.825 + } 1.826 + } 1.827 + } 1.828 + } 1.829 + } 1.830 + return TRUE; 1.831 +} 1.832 + 1.833 +TimeZoneNames::MatchInfoCollection* 1.834 +ZNameSearchHandler::getMatches(int32_t& maxMatchLen) { 1.835 + // give the ownership to the caller 1.836 + TimeZoneNames::MatchInfoCollection* results = fResults; 1.837 + maxMatchLen = fMaxMatchLen; 1.838 + 1.839 + // reset 1.840 + fResults = NULL; 1.841 + fMaxMatchLen = 0; 1.842 + return results; 1.843 +} 1.844 + 1.845 +// --------------------------------------------------- 1.846 +// TimeZoneNamesImpl 1.847 +// 1.848 +// TimeZoneNames implementation class. This is the main 1.849 +// part of this module. 1.850 +// --------------------------------------------------- 1.851 + 1.852 +U_CDECL_BEGIN 1.853 +/** 1.854 + * Deleter for ZNames 1.855 + */ 1.856 +static void U_CALLCONV 1.857 +deleteZNames(void *obj) { 1.858 + if (obj != EMPTY) { 1.859 + delete (ZNames *)obj; 1.860 + } 1.861 +} 1.862 +/** 1.863 + * Deleter for TZNames 1.864 + */ 1.865 +static void U_CALLCONV 1.866 +deleteTZNames(void *obj) { 1.867 + if (obj != EMPTY) { 1.868 + delete (TZNames *)obj; 1.869 + } 1.870 +} 1.871 + 1.872 +/** 1.873 + * Deleter for ZNameInfo 1.874 + */ 1.875 +static void U_CALLCONV 1.876 +deleteZNameInfo(void *obj) { 1.877 + uprv_free(obj); 1.878 +} 1.879 + 1.880 +U_CDECL_END 1.881 + 1.882 +static UMutex gLock = U_MUTEX_INITIALIZER; 1.883 + 1.884 +TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status) 1.885 +: fLocale(locale), 1.886 + fZoneStrings(NULL), 1.887 + fTZNamesMap(NULL), 1.888 + fMZNamesMap(NULL), 1.889 + fNamesTrieFullyLoaded(FALSE), 1.890 + fNamesTrie(TRUE, deleteZNameInfo) { 1.891 + initialize(locale, status); 1.892 +} 1.893 + 1.894 +void 1.895 +TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) { 1.896 + if (U_FAILURE(status)) { 1.897 + return; 1.898 + } 1.899 + 1.900 + // Load zoneStrings bundle 1.901 + UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. 1.902 + fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); 1.903 + fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts); 1.904 + if (U_FAILURE(tmpsts)) { 1.905 + status = tmpsts; 1.906 + cleanup(); 1.907 + return; 1.908 + } 1.909 + 1.910 + // Initialize hashtables holding time zone/meta zone names 1.911 + fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 1.912 + fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 1.913 + if (U_FAILURE(status)) { 1.914 + cleanup(); 1.915 + return; 1.916 + } 1.917 + 1.918 + uhash_setValueDeleter(fMZNamesMap, deleteZNames); 1.919 + uhash_setValueDeleter(fTZNamesMap, deleteTZNames); 1.920 + // no key deleters for name maps 1.921 + 1.922 + // preload zone strings for the default zone 1.923 + TimeZone *tz = TimeZone::createDefault(); 1.924 + const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); 1.925 + if (tzID != NULL) { 1.926 + loadStrings(UnicodeString(tzID)); 1.927 + } 1.928 + delete tz; 1.929 + 1.930 + return; 1.931 +} 1.932 + 1.933 +/* 1.934 + * This method updates the cache and must be called with a lock, 1.935 + * except initializer. 1.936 + */ 1.937 +void 1.938 +TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) { 1.939 + loadTimeZoneNames(tzCanonicalID); 1.940 + 1.941 + UErrorCode status = U_ZERO_ERROR; 1.942 + StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status); 1.943 + if (U_SUCCESS(status) && mzIDs != NULL) { 1.944 + const UnicodeString *mzID; 1.945 + while ((mzID = mzIDs->snext(status))) { 1.946 + if (U_FAILURE(status)) { 1.947 + break; 1.948 + } 1.949 + loadMetaZoneNames(*mzID); 1.950 + } 1.951 + delete mzIDs; 1.952 + } 1.953 +} 1.954 + 1.955 +TimeZoneNamesImpl::~TimeZoneNamesImpl() { 1.956 + cleanup(); 1.957 +} 1.958 + 1.959 +void 1.960 +TimeZoneNamesImpl::cleanup() { 1.961 + if (fZoneStrings != NULL) { 1.962 + ures_close(fZoneStrings); 1.963 + fZoneStrings = NULL; 1.964 + } 1.965 + if (fMZNamesMap != NULL) { 1.966 + uhash_close(fMZNamesMap); 1.967 + fMZNamesMap = NULL; 1.968 + } 1.969 + if (fTZNamesMap != NULL) { 1.970 + uhash_close(fTZNamesMap); 1.971 + fTZNamesMap = NULL; 1.972 + } 1.973 +} 1.974 + 1.975 +UBool 1.976 +TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const { 1.977 + if (this == &other) { 1.978 + return TRUE; 1.979 + } 1.980 + // No implementation for now 1.981 + return FALSE; 1.982 +} 1.983 + 1.984 +TimeZoneNames* 1.985 +TimeZoneNamesImpl::clone() const { 1.986 + UErrorCode status = U_ZERO_ERROR; 1.987 + return new TimeZoneNamesImpl(fLocale, status); 1.988 +} 1.989 + 1.990 +StringEnumeration* 1.991 +TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const { 1.992 + if (U_FAILURE(status)) { 1.993 + return NULL; 1.994 + } 1.995 + const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs(); 1.996 + if (mzIDs == NULL) { 1.997 + return new MetaZoneIDsEnumeration(); 1.998 + } 1.999 + return new MetaZoneIDsEnumeration(*mzIDs); 1.1000 +} 1.1001 + 1.1002 +StringEnumeration* 1.1003 +TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 1.1004 + if (U_FAILURE(status)) { 1.1005 + return NULL; 1.1006 + } 1.1007 + const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID); 1.1008 + if (mappings == NULL) { 1.1009 + return new MetaZoneIDsEnumeration(); 1.1010 + } 1.1011 + 1.1012 + MetaZoneIDsEnumeration *senum = NULL; 1.1013 + UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status); 1.1014 + if (mzIDs == NULL) { 1.1015 + status = U_MEMORY_ALLOCATION_ERROR; 1.1016 + } 1.1017 + if (U_SUCCESS(status)) { 1.1018 + U_ASSERT(mzIDs != NULL); 1.1019 + for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) { 1.1020 + 1.1021 + OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i); 1.1022 + const UChar *mzID = map->mzid; 1.1023 + if (!mzIDs->contains((void *)mzID)) { 1.1024 + mzIDs->addElement((void *)mzID, status); 1.1025 + } 1.1026 + } 1.1027 + if (U_SUCCESS(status)) { 1.1028 + senum = new MetaZoneIDsEnumeration(mzIDs); 1.1029 + } else { 1.1030 + delete mzIDs; 1.1031 + } 1.1032 + } 1.1033 + return senum; 1.1034 +} 1.1035 + 1.1036 +UnicodeString& 1.1037 +TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 1.1038 + ZoneMeta::getMetazoneID(tzID, date, mzID); 1.1039 + return mzID; 1.1040 +} 1.1041 + 1.1042 +UnicodeString& 1.1043 +TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 1.1044 + ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID); 1.1045 + return tzID; 1.1046 +} 1.1047 + 1.1048 +UnicodeString& 1.1049 +TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID, 1.1050 + UTimeZoneNameType type, 1.1051 + UnicodeString& name) const { 1.1052 + name.setToBogus(); // cleanup result. 1.1053 + if (mzID.isEmpty()) { 1.1054 + return name; 1.1055 + } 1.1056 + 1.1057 + ZNames *znames = NULL; 1.1058 + TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1.1059 + 1.1060 + umtx_lock(&gLock); 1.1061 + { 1.1062 + znames = nonConstThis->loadMetaZoneNames(mzID); 1.1063 + } 1.1064 + umtx_unlock(&gLock); 1.1065 + 1.1066 + if (znames != NULL) { 1.1067 + const UChar* s = znames->getName(type); 1.1068 + if (s != NULL) { 1.1069 + name.setTo(TRUE, s, -1); 1.1070 + } 1.1071 + } 1.1072 + return name; 1.1073 +} 1.1074 + 1.1075 +UnicodeString& 1.1076 +TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 1.1077 + name.setToBogus(); // cleanup result. 1.1078 + if (tzID.isEmpty()) { 1.1079 + return name; 1.1080 + } 1.1081 + 1.1082 + TZNames *tznames = NULL; 1.1083 + TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1.1084 + 1.1085 + umtx_lock(&gLock); 1.1086 + { 1.1087 + tznames = nonConstThis->loadTimeZoneNames(tzID); 1.1088 + } 1.1089 + umtx_unlock(&gLock); 1.1090 + 1.1091 + if (tznames != NULL) { 1.1092 + const UChar *s = tznames->getName(type); 1.1093 + if (s != NULL) { 1.1094 + name.setTo(TRUE, s, -1); 1.1095 + } 1.1096 + } 1.1097 + return name; 1.1098 +} 1.1099 + 1.1100 +UnicodeString& 1.1101 +TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 1.1102 + name.setToBogus(); // cleanup result. 1.1103 + const UChar* locName = NULL; 1.1104 + TZNames *tznames = NULL; 1.1105 + TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1.1106 + 1.1107 + umtx_lock(&gLock); 1.1108 + { 1.1109 + tznames = nonConstThis->loadTimeZoneNames(tzID); 1.1110 + } 1.1111 + umtx_unlock(&gLock); 1.1112 + 1.1113 + if (tznames != NULL) { 1.1114 + locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION); 1.1115 + } 1.1116 + if (locName != NULL) { 1.1117 + name.setTo(TRUE, locName, -1); 1.1118 + } 1.1119 + 1.1120 + return name; 1.1121 +} 1.1122 + 1.1123 + 1.1124 +// Merge the MZ_PREFIX and mzId 1.1125 +static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) { 1.1126 + if (mzID.isEmpty()) { 1.1127 + result[0] = '\0'; 1.1128 + return; 1.1129 + } 1.1130 + 1.1131 + char mzIdChar[ZID_KEY_MAX + 1]; 1.1132 + int32_t keyLen; 1.1133 + int32_t prefixLen = uprv_strlen(gMZPrefix); 1.1134 + keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV); 1.1135 + uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen); 1.1136 + uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen); 1.1137 + result[keyLen + prefixLen] = '\0'; 1.1138 +} 1.1139 + 1.1140 +/* 1.1141 + * This method updates the cache and must be called with a lock 1.1142 + */ 1.1143 +ZNames* 1.1144 +TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) { 1.1145 + if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) { 1.1146 + return NULL; 1.1147 + } 1.1148 + 1.1149 + ZNames *znames = NULL; 1.1150 + 1.1151 + UErrorCode status = U_ZERO_ERROR; 1.1152 + UChar mzIDKey[ZID_KEY_MAX + 1]; 1.1153 + mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status); 1.1154 + U_ASSERT(status == U_ZERO_ERROR); // already checked length above 1.1155 + mzIDKey[mzID.length()] = 0; 1.1156 + 1.1157 + void *cacheVal = uhash_get(fMZNamesMap, mzIDKey); 1.1158 + if (cacheVal == NULL) { 1.1159 + char key[ZID_KEY_MAX + 1]; 1.1160 + mergeTimeZoneKey(mzID, key); 1.1161 + znames = ZNames::createInstance(fZoneStrings, key); 1.1162 + 1.1163 + if (znames == NULL) { 1.1164 + cacheVal = (void *)EMPTY; 1.1165 + } else { 1.1166 + cacheVal = znames; 1.1167 + } 1.1168 + // Use the persistent ID as the resource key, so we can 1.1169 + // avoid duplications. 1.1170 + const UChar* newKey = ZoneMeta::findMetaZoneID(mzID); 1.1171 + if (newKey != NULL) { 1.1172 + uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status); 1.1173 + if (U_FAILURE(status)) { 1.1174 + if (znames != NULL) { 1.1175 + delete znames; 1.1176 + } 1.1177 + } else if (znames != NULL) { 1.1178 + // put the name info into the trie 1.1179 + for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) { 1.1180 + const UChar* name = znames->getName(ALL_NAME_TYPES[i]); 1.1181 + if (name != NULL) { 1.1182 + ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo)); 1.1183 + if (nameinfo != NULL) { 1.1184 + nameinfo->type = ALL_NAME_TYPES[i]; 1.1185 + nameinfo->tzID = NULL; 1.1186 + nameinfo->mzID = newKey; 1.1187 + fNamesTrie.put(name, nameinfo, status); 1.1188 + } 1.1189 + } 1.1190 + } 1.1191 + } 1.1192 + 1.1193 + } else { 1.1194 + // Should never happen with a valid input 1.1195 + if (znames != NULL) { 1.1196 + // It's not possible that we get a valid ZNames with unknown ID. 1.1197 + // But just in case.. 1.1198 + delete znames; 1.1199 + znames = NULL; 1.1200 + } 1.1201 + } 1.1202 + } else if (cacheVal != EMPTY) { 1.1203 + znames = (ZNames *)cacheVal; 1.1204 + } 1.1205 + 1.1206 + return znames; 1.1207 +} 1.1208 + 1.1209 +/* 1.1210 + * This method updates the cache and must be called with a lock 1.1211 + */ 1.1212 +TZNames* 1.1213 +TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) { 1.1214 + if (tzID.length() > ZID_KEY_MAX) { 1.1215 + return NULL; 1.1216 + } 1.1217 + 1.1218 + TZNames *tznames = NULL; 1.1219 + 1.1220 + UErrorCode status = U_ZERO_ERROR; 1.1221 + UChar tzIDKey[ZID_KEY_MAX + 1]; 1.1222 + int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status); 1.1223 + U_ASSERT(status == U_ZERO_ERROR); // already checked length above 1.1224 + tzIDKey[tzIDKeyLen] = 0; 1.1225 + 1.1226 + void *cacheVal = uhash_get(fTZNamesMap, tzIDKey); 1.1227 + if (cacheVal == NULL) { 1.1228 + char key[ZID_KEY_MAX + 1]; 1.1229 + UErrorCode status = U_ZERO_ERROR; 1.1230 + // Replace "/" with ":". 1.1231 + UnicodeString uKey(tzID); 1.1232 + for (int32_t i = 0; i < uKey.length(); i++) { 1.1233 + if (uKey.charAt(i) == (UChar)0x2F) { 1.1234 + uKey.setCharAt(i, (UChar)0x3A); 1.1235 + } 1.1236 + } 1.1237 + uKey.extract(0, uKey.length(), key, sizeof(key), US_INV); 1.1238 + tznames = TZNames::createInstance(fZoneStrings, key, tzID); 1.1239 + 1.1240 + if (tznames == NULL) { 1.1241 + cacheVal = (void *)EMPTY; 1.1242 + } else { 1.1243 + cacheVal = tznames; 1.1244 + } 1.1245 + // Use the persistent ID as the resource key, so we can 1.1246 + // avoid duplications. 1.1247 + const UChar* newKey = ZoneMeta::findTimeZoneID(tzID); 1.1248 + if (newKey != NULL) { 1.1249 + uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status); 1.1250 + if (U_FAILURE(status)) { 1.1251 + if (tznames != NULL) { 1.1252 + delete tznames; 1.1253 + } 1.1254 + } else if (tznames != NULL) { 1.1255 + // put the name info into the trie 1.1256 + for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) { 1.1257 + const UChar* name = tznames->getName(ALL_NAME_TYPES[i]); 1.1258 + if (name != NULL) { 1.1259 + ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo)); 1.1260 + if (nameinfo != NULL) { 1.1261 + nameinfo->type = ALL_NAME_TYPES[i]; 1.1262 + nameinfo->tzID = newKey; 1.1263 + nameinfo->mzID = NULL; 1.1264 + fNamesTrie.put(name, nameinfo, status); 1.1265 + } 1.1266 + } 1.1267 + } 1.1268 + } 1.1269 + } else { 1.1270 + // Should never happen with a valid input 1.1271 + if (tznames != NULL) { 1.1272 + // It's not possible that we get a valid TZNames with unknown ID. 1.1273 + // But just in case.. 1.1274 + delete tznames; 1.1275 + tznames = NULL; 1.1276 + } 1.1277 + } 1.1278 + } else if (cacheVal != EMPTY) { 1.1279 + tznames = (TZNames *)cacheVal; 1.1280 + } 1.1281 + 1.1282 + return tznames; 1.1283 +} 1.1284 + 1.1285 +TimeZoneNames::MatchInfoCollection* 1.1286 +TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1.1287 + ZNameSearchHandler handler(types); 1.1288 + 1.1289 + TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1.1290 + 1.1291 + umtx_lock(&gLock); 1.1292 + { 1.1293 + fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1.1294 + } 1.1295 + umtx_unlock(&gLock); 1.1296 + 1.1297 + if (U_FAILURE(status)) { 1.1298 + return NULL; 1.1299 + } 1.1300 + 1.1301 + int32_t maxLen = 0; 1.1302 + TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen); 1.1303 + if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) { 1.1304 + // perfect match 1.1305 + return matches; 1.1306 + } 1.1307 + 1.1308 + delete matches; 1.1309 + 1.1310 + // All names are not yet loaded into the trie 1.1311 + umtx_lock(&gLock); 1.1312 + { 1.1313 + if (!fNamesTrieFullyLoaded) { 1.1314 + const UnicodeString *id; 1.1315 + 1.1316 + // load strings for all zones 1.1317 + StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1.1318 + if (U_SUCCESS(status)) { 1.1319 + while ((id = tzIDs->snext(status))) { 1.1320 + if (U_FAILURE(status)) { 1.1321 + break; 1.1322 + } 1.1323 + // loadStrings also load related metazone strings 1.1324 + nonConstThis->loadStrings(*id); 1.1325 + } 1.1326 + } 1.1327 + if (tzIDs != NULL) { 1.1328 + delete tzIDs; 1.1329 + } 1.1330 + if (U_SUCCESS(status)) { 1.1331 + nonConstThis->fNamesTrieFullyLoaded = TRUE; 1.1332 + } 1.1333 + } 1.1334 + } 1.1335 + umtx_unlock(&gLock); 1.1336 + 1.1337 + if (U_FAILURE(status)) { 1.1338 + return NULL; 1.1339 + } 1.1340 + 1.1341 + umtx_lock(&gLock); 1.1342 + { 1.1343 + // now try it again 1.1344 + fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1.1345 + } 1.1346 + umtx_unlock(&gLock); 1.1347 + 1.1348 + return handler.getMatches(maxLen); 1.1349 +} 1.1350 + 1.1351 +static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/" 1.1352 +static const int32_t gEtcPrefixLen = 4; 1.1353 +static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/ 1.1354 +static const int32_t gSystemVPrefixLen = 8; 1.1355 +static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8" 1.1356 +static const int32_t gRiyadh8Len = 7; 1.1357 + 1.1358 +UnicodeString& U_EXPORT2 1.1359 +TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) { 1.1360 + if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen) 1.1361 + || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) { 1.1362 + name.setToBogus(); 1.1363 + return name; 1.1364 + } 1.1365 + 1.1366 + int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */); 1.1367 + if (sep > 0 && sep + 1 < tzID.length()) { 1.1368 + name.setTo(tzID, sep + 1); 1.1369 + name.findAndReplace(UnicodeString((UChar)0x5f /* _ */), 1.1370 + UnicodeString((UChar)0x20 /* space */)); 1.1371 + } else { 1.1372 + name.setToBogus(); 1.1373 + } 1.1374 + return name; 1.1375 +} 1.1376 + 1.1377 +U_NAMESPACE_END 1.1378 + 1.1379 + 1.1380 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.1381 + 1.1382 +//eof