intl/icu/source/i18n/tznames_impl.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 *******************************************************************************
michael@0 3 * Copyright (C) 2011-2013, International Business Machines Corporation and
michael@0 4 * others. All Rights Reserved.
michael@0 5 *******************************************************************************
michael@0 6 *
michael@0 7 * File TZNAMES_IMPL.CPP
michael@0 8 *
michael@0 9 *******************************************************************************
michael@0 10 */
michael@0 11
michael@0 12 #include "unicode/utypes.h"
michael@0 13
michael@0 14 #if !UCONFIG_NO_FORMATTING
michael@0 15
michael@0 16 #include "unicode/ustring.h"
michael@0 17 #include "unicode/timezone.h"
michael@0 18
michael@0 19 #include "tznames_impl.h"
michael@0 20 #include "cmemory.h"
michael@0 21 #include "cstring.h"
michael@0 22 #include "uassert.h"
michael@0 23 #include "mutex.h"
michael@0 24 #include "uresimp.h"
michael@0 25 #include "ureslocs.h"
michael@0 26 #include "zonemeta.h"
michael@0 27 #include "ucln_in.h"
michael@0 28 #include "uvector.h"
michael@0 29 #include "olsontz.h"
michael@0 30
michael@0 31
michael@0 32 U_NAMESPACE_BEGIN
michael@0 33
michael@0 34 #define ZID_KEY_MAX 128
michael@0 35 #define MZ_PREFIX_LEN 5
michael@0 36
michael@0 37 static const char gZoneStrings[] = "zoneStrings";
michael@0 38 static const char gMZPrefix[] = "meta:";
michael@0 39
michael@0 40 static const char* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"};
michael@0 41 static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
michael@0 42
michael@0 43 static const char gEcTag[] = "ec";
michael@0 44
michael@0 45 static const char EMPTY[] = "<empty>"; // place holder for empty ZNames/TZNames
michael@0 46
michael@0 47 static const UTimeZoneNameType ALL_NAME_TYPES[] = {
michael@0 48 UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT,
michael@0 49 UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT,
michael@0 50 UTZNM_EXEMPLAR_LOCATION,
michael@0 51 UTZNM_UNKNOWN // unknown as the last one
michael@0 52 };
michael@0 53
michael@0 54 #define DEFAULT_CHARACTERNODE_CAPACITY 1
michael@0 55
michael@0 56 // ---------------------------------------------------
michael@0 57 // CharacterNode class implementation
michael@0 58 // ---------------------------------------------------
michael@0 59 void CharacterNode::clear() {
michael@0 60 uprv_memset(this, 0, sizeof(*this));
michael@0 61 }
michael@0 62
michael@0 63 void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
michael@0 64 if (fValues == NULL) {
michael@0 65 // Do nothing.
michael@0 66 } else if (!fHasValuesVector) {
michael@0 67 if (valueDeleter) {
michael@0 68 valueDeleter(fValues);
michael@0 69 }
michael@0 70 } else {
michael@0 71 delete (UVector *)fValues;
michael@0 72 }
michael@0 73 }
michael@0 74
michael@0 75 void
michael@0 76 CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
michael@0 77 if (U_FAILURE(status)) {
michael@0 78 if (valueDeleter) {
michael@0 79 valueDeleter(value);
michael@0 80 }
michael@0 81 return;
michael@0 82 }
michael@0 83 if (fValues == NULL) {
michael@0 84 fValues = value;
michael@0 85 } else {
michael@0 86 // At least one value already.
michael@0 87 if (!fHasValuesVector) {
michael@0 88 // There is only one value so far, and not in a vector yet.
michael@0 89 // Create a vector and add the old value.
michael@0 90 UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
michael@0 91 if (U_FAILURE(status)) {
michael@0 92 if (valueDeleter) {
michael@0 93 valueDeleter(value);
michael@0 94 }
michael@0 95 return;
michael@0 96 }
michael@0 97 values->addElement(fValues, status);
michael@0 98 fValues = values;
michael@0 99 fHasValuesVector = TRUE;
michael@0 100 }
michael@0 101 // Add the new value.
michael@0 102 ((UVector *)fValues)->addElement(value, status);
michael@0 103 }
michael@0 104 }
michael@0 105
michael@0 106 // ---------------------------------------------------
michael@0 107 // TextTrieMapSearchResultHandler class implementation
michael@0 108 // ---------------------------------------------------
michael@0 109 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
michael@0 110 }
michael@0 111
michael@0 112 // ---------------------------------------------------
michael@0 113 // TextTrieMap class implementation
michael@0 114 // ---------------------------------------------------
michael@0 115 TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
michael@0 116 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
michael@0 117 fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
michael@0 118 }
michael@0 119
michael@0 120 TextTrieMap::~TextTrieMap() {
michael@0 121 int32_t index;
michael@0 122 for (index = 0; index < fNodesCount; ++index) {
michael@0 123 fNodes[index].deleteValues(fValueDeleter);
michael@0 124 }
michael@0 125 uprv_free(fNodes);
michael@0 126 if (fLazyContents != NULL) {
michael@0 127 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
michael@0 128 if (fValueDeleter) {
michael@0 129 fValueDeleter(fLazyContents->elementAt(i+1));
michael@0 130 }
michael@0 131 }
michael@0 132 delete fLazyContents;
michael@0 133 }
michael@0 134 }
michael@0 135
michael@0 136 int32_t TextTrieMap::isEmpty() const {
michael@0 137 // Use a separate field for fIsEmpty because it will remain unchanged once the
michael@0 138 // Trie is built, while fNodes and fLazyContents change with the lazy init
michael@0 139 // of the nodes structure. Trying to test the changing fields has
michael@0 140 // thread safety complications.
michael@0 141 return fIsEmpty;
michael@0 142 }
michael@0 143
michael@0 144
michael@0 145 // We defer actually building the TextTrieMap node structure until the first time a
michael@0 146 // search is performed. put() simply saves the parameters in case we do
michael@0 147 // eventually need to build it.
michael@0 148 //
michael@0 149 void
michael@0 150 TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
michael@0 151 const UChar *s = sp.get(key, status);
michael@0 152 put(s, value, status);
michael@0 153 }
michael@0 154
michael@0 155 // This method is for designed for a persistent key, such as string key stored in
michael@0 156 // resource bundle.
michael@0 157 void
michael@0 158 TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
michael@0 159 fIsEmpty = FALSE;
michael@0 160 if (fLazyContents == NULL) {
michael@0 161 fLazyContents = new UVector(status);
michael@0 162 if (fLazyContents == NULL) {
michael@0 163 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 164 }
michael@0 165 }
michael@0 166 if (U_FAILURE(status)) {
michael@0 167 return;
michael@0 168 }
michael@0 169 U_ASSERT(fLazyContents != NULL);
michael@0 170 UChar *s = const_cast<UChar *>(key);
michael@0 171 fLazyContents->addElement(s, status);
michael@0 172 fLazyContents->addElement(value, status);
michael@0 173 }
michael@0 174
michael@0 175 void
michael@0 176 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
michael@0 177 if (fNodes == NULL) {
michael@0 178 fNodesCapacity = 512;
michael@0 179 fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
michael@0 180 fNodes[0].clear(); // Init root node.
michael@0 181 fNodesCount = 1;
michael@0 182 }
michael@0 183
michael@0 184 UnicodeString foldedKey;
michael@0 185 const UChar *keyBuffer;
michael@0 186 int32_t keyLength;
michael@0 187 if (fIgnoreCase) {
michael@0 188 // Ok to use fastCopyFrom() because we discard the copy when we return.
michael@0 189 foldedKey.fastCopyFrom(key).foldCase();
michael@0 190 keyBuffer = foldedKey.getBuffer();
michael@0 191 keyLength = foldedKey.length();
michael@0 192 } else {
michael@0 193 keyBuffer = key.getBuffer();
michael@0 194 keyLength = key.length();
michael@0 195 }
michael@0 196
michael@0 197 CharacterNode *node = fNodes;
michael@0 198 int32_t index;
michael@0 199 for (index = 0; index < keyLength; ++index) {
michael@0 200 node = addChildNode(node, keyBuffer[index], status);
michael@0 201 }
michael@0 202 node->addValue(value, fValueDeleter, status);
michael@0 203 }
michael@0 204
michael@0 205 UBool
michael@0 206 TextTrieMap::growNodes() {
michael@0 207 if (fNodesCapacity == 0xffff) {
michael@0 208 return FALSE; // We use 16-bit node indexes.
michael@0 209 }
michael@0 210 int32_t newCapacity = fNodesCapacity + 1000;
michael@0 211 if (newCapacity > 0xffff) {
michael@0 212 newCapacity = 0xffff;
michael@0 213 }
michael@0 214 CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
michael@0 215 if (newNodes == NULL) {
michael@0 216 return FALSE;
michael@0 217 }
michael@0 218 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
michael@0 219 uprv_free(fNodes);
michael@0 220 fNodes = newNodes;
michael@0 221 fNodesCapacity = newCapacity;
michael@0 222 return TRUE;
michael@0 223 }
michael@0 224
michael@0 225 CharacterNode*
michael@0 226 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
michael@0 227 if (U_FAILURE(status)) {
michael@0 228 return NULL;
michael@0 229 }
michael@0 230 // Linear search of the sorted list of children.
michael@0 231 uint16_t prevIndex = 0;
michael@0 232 uint16_t nodeIndex = parent->fFirstChild;
michael@0 233 while (nodeIndex > 0) {
michael@0 234 CharacterNode *current = fNodes + nodeIndex;
michael@0 235 UChar childCharacter = current->fCharacter;
michael@0 236 if (childCharacter == c) {
michael@0 237 return current;
michael@0 238 } else if (childCharacter > c) {
michael@0 239 break;
michael@0 240 }
michael@0 241 prevIndex = nodeIndex;
michael@0 242 nodeIndex = current->fNextSibling;
michael@0 243 }
michael@0 244
michael@0 245 // Ensure capacity. Grow fNodes[] if needed.
michael@0 246 if (fNodesCount == fNodesCapacity) {
michael@0 247 int32_t parentIndex = (int32_t)(parent - fNodes);
michael@0 248 if (!growNodes()) {
michael@0 249 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 250 return NULL;
michael@0 251 }
michael@0 252 parent = fNodes + parentIndex;
michael@0 253 }
michael@0 254
michael@0 255 // Insert a new child node with c in sorted order.
michael@0 256 CharacterNode *node = fNodes + fNodesCount;
michael@0 257 node->clear();
michael@0 258 node->fCharacter = c;
michael@0 259 node->fNextSibling = nodeIndex;
michael@0 260 if (prevIndex == 0) {
michael@0 261 parent->fFirstChild = (uint16_t)fNodesCount;
michael@0 262 } else {
michael@0 263 fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
michael@0 264 }
michael@0 265 ++fNodesCount;
michael@0 266 return node;
michael@0 267 }
michael@0 268
michael@0 269 CharacterNode*
michael@0 270 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
michael@0 271 // Linear search of the sorted list of children.
michael@0 272 uint16_t nodeIndex = parent->fFirstChild;
michael@0 273 while (nodeIndex > 0) {
michael@0 274 CharacterNode *current = fNodes + nodeIndex;
michael@0 275 UChar childCharacter = current->fCharacter;
michael@0 276 if (childCharacter == c) {
michael@0 277 return current;
michael@0 278 } else if (childCharacter > c) {
michael@0 279 break;
michael@0 280 }
michael@0 281 nodeIndex = current->fNextSibling;
michael@0 282 }
michael@0 283 return NULL;
michael@0 284 }
michael@0 285
michael@0 286 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
michael@0 287 static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
michael@0 288
michael@0 289 // buildTrie() - The Trie node structure is needed. Create it from the data that was
michael@0 290 // saved at the time the ZoneStringFormatter was created. The Trie is only
michael@0 291 // needed for parsing operations, which are less common than formatting,
michael@0 292 // and the Trie is big, which is why its creation is deferred until first use.
michael@0 293 void TextTrieMap::buildTrie(UErrorCode &status) {
michael@0 294 if (fLazyContents != NULL) {
michael@0 295 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
michael@0 296 const UChar *key = (UChar *)fLazyContents->elementAt(i);
michael@0 297 void *val = fLazyContents->elementAt(i+1);
michael@0 298 UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor.
michael@0 299 putImpl(keyString, val, status);
michael@0 300 }
michael@0 301 delete fLazyContents;
michael@0 302 fLazyContents = NULL;
michael@0 303 }
michael@0 304 }
michael@0 305
michael@0 306 void
michael@0 307 TextTrieMap::search(const UnicodeString &text, int32_t start,
michael@0 308 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
michael@0 309 {
michael@0 310 // TODO: if locking the mutex for each check proves to be a performance problem,
michael@0 311 // add a flag of type atomic_int32_t to class TextTrieMap, and use only
michael@0 312 // the ICU atomic safe functions for assigning and testing.
michael@0 313 // Don't test the pointer fLazyContents.
michael@0 314 // Don't do unless it's really required.
michael@0 315 Mutex lock(&TextTrieMutex);
michael@0 316 if (fLazyContents != NULL) {
michael@0 317 TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
michael@0 318 nonConstThis->buildTrie(status);
michael@0 319 }
michael@0 320 }
michael@0 321 if (fNodes == NULL) {
michael@0 322 return;
michael@0 323 }
michael@0 324 search(fNodes, text, start, start, handler, status);
michael@0 325 }
michael@0 326
michael@0 327 void
michael@0 328 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
michael@0 329 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
michael@0 330 if (U_FAILURE(status)) {
michael@0 331 return;
michael@0 332 }
michael@0 333 if (node->hasValues()) {
michael@0 334 if (!handler->handleMatch(index - start, node, status)) {
michael@0 335 return;
michael@0 336 }
michael@0 337 if (U_FAILURE(status)) {
michael@0 338 return;
michael@0 339 }
michael@0 340 }
michael@0 341 UChar32 c = text.char32At(index);
michael@0 342 if (fIgnoreCase) {
michael@0 343 // size of character may grow after fold operation
michael@0 344 UnicodeString tmp(c);
michael@0 345 tmp.foldCase();
michael@0 346 int32_t tmpidx = 0;
michael@0 347 while (tmpidx < tmp.length()) {
michael@0 348 c = tmp.char32At(tmpidx);
michael@0 349 node = getChildNode(node, c);
michael@0 350 if (node == NULL) {
michael@0 351 break;
michael@0 352 }
michael@0 353 tmpidx = tmp.moveIndex32(tmpidx, 1);
michael@0 354 }
michael@0 355 } else {
michael@0 356 node = getChildNode(node, c);
michael@0 357 }
michael@0 358 if (node != NULL) {
michael@0 359 search(node, text, start, index+1, handler, status);
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 // ---------------------------------------------------
michael@0 364 // ZNStringPool class implementation
michael@0 365 // ---------------------------------------------------
michael@0 366 static const int32_t POOL_CHUNK_SIZE = 2000;
michael@0 367 struct ZNStringPoolChunk: public UMemory {
michael@0 368 ZNStringPoolChunk *fNext; // Ptr to next pool chunk
michael@0 369 int32_t fLimit; // Index to start of unused area at end of fStrings
michael@0 370 UChar fStrings[POOL_CHUNK_SIZE]; // Strings array
michael@0 371 ZNStringPoolChunk();
michael@0 372 };
michael@0 373
michael@0 374 ZNStringPoolChunk::ZNStringPoolChunk() {
michael@0 375 fNext = NULL;
michael@0 376 fLimit = 0;
michael@0 377 }
michael@0 378
michael@0 379 ZNStringPool::ZNStringPool(UErrorCode &status) {
michael@0 380 fChunks = NULL;
michael@0 381 fHash = NULL;
michael@0 382 if (U_FAILURE(status)) {
michael@0 383 return;
michael@0 384 }
michael@0 385 fChunks = new ZNStringPoolChunk;
michael@0 386 if (fChunks == NULL) {
michael@0 387 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 388 return;
michael@0 389 }
michael@0 390
michael@0 391 fHash = uhash_open(uhash_hashUChars /* keyHash */,
michael@0 392 uhash_compareUChars /* keyComp */,
michael@0 393 uhash_compareUChars /* valueComp */,
michael@0 394 &status);
michael@0 395 if (U_FAILURE(status)) {
michael@0 396 return;
michael@0 397 }
michael@0 398 }
michael@0 399
michael@0 400 ZNStringPool::~ZNStringPool() {
michael@0 401 if (fHash != NULL) {
michael@0 402 uhash_close(fHash);
michael@0 403 fHash = NULL;
michael@0 404 }
michael@0 405
michael@0 406 while (fChunks != NULL) {
michael@0 407 ZNStringPoolChunk *nextChunk = fChunks->fNext;
michael@0 408 delete fChunks;
michael@0 409 fChunks = nextChunk;
michael@0 410 }
michael@0 411 }
michael@0 412
michael@0 413 static const UChar EmptyString = 0;
michael@0 414
michael@0 415 const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
michael@0 416 const UChar *pooledString;
michael@0 417 if (U_FAILURE(status)) {
michael@0 418 return &EmptyString;
michael@0 419 }
michael@0 420
michael@0 421 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
michael@0 422 if (pooledString != NULL) {
michael@0 423 return pooledString;
michael@0 424 }
michael@0 425
michael@0 426 int32_t length = u_strlen(s);
michael@0 427 int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
michael@0 428 if (remainingLength <= length) {
michael@0 429 U_ASSERT(length < POOL_CHUNK_SIZE);
michael@0 430 if (length >= POOL_CHUNK_SIZE) {
michael@0 431 status = U_INTERNAL_PROGRAM_ERROR;
michael@0 432 return &EmptyString;
michael@0 433 }
michael@0 434 ZNStringPoolChunk *oldChunk = fChunks;
michael@0 435 fChunks = new ZNStringPoolChunk;
michael@0 436 if (fChunks == NULL) {
michael@0 437 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 438 return &EmptyString;
michael@0 439 }
michael@0 440 fChunks->fNext = oldChunk;
michael@0 441 }
michael@0 442
michael@0 443 UChar *destString = &fChunks->fStrings[fChunks->fLimit];
michael@0 444 u_strcpy(destString, s);
michael@0 445 fChunks->fLimit += (length + 1);
michael@0 446 uhash_put(fHash, destString, destString, &status);
michael@0 447 return destString;
michael@0 448 }
michael@0 449
michael@0 450
michael@0 451 //
michael@0 452 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
michael@0 453 // into the pool's storage. Used for strings from resource bundles,
michael@0 454 // which will perisist for the life of the zone string formatter, and
michael@0 455 // therefore can be used directly without copying.
michael@0 456 const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
michael@0 457 const UChar *pooledString;
michael@0 458 if (U_FAILURE(status)) {
michael@0 459 return &EmptyString;
michael@0 460 }
michael@0 461 if (s != NULL) {
michael@0 462 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
michael@0 463 if (pooledString == NULL) {
michael@0 464 UChar *ncs = const_cast<UChar *>(s);
michael@0 465 uhash_put(fHash, ncs, ncs, &status);
michael@0 466 }
michael@0 467 }
michael@0 468 return s;
michael@0 469 }
michael@0 470
michael@0 471
michael@0 472 const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
michael@0 473 UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
michael@0 474 return this->get(nonConstStr.getTerminatedBuffer(), status);
michael@0 475 }
michael@0 476
michael@0 477 /*
michael@0 478 * freeze(). Close the hash table that maps to the pooled strings.
michael@0 479 * After freezing, the pool can not be searched or added to,
michael@0 480 * but all existing references to pooled strings remain valid.
michael@0 481 *
michael@0 482 * The main purpose is to recover the storage used for the hash.
michael@0 483 */
michael@0 484 void ZNStringPool::freeze() {
michael@0 485 uhash_close(fHash);
michael@0 486 fHash = NULL;
michael@0 487 }
michael@0 488
michael@0 489
michael@0 490 // ---------------------------------------------------
michael@0 491 // ZNames - names common for time zone and meta zone
michael@0 492 // ---------------------------------------------------
michael@0 493 class ZNames : public UMemory {
michael@0 494 public:
michael@0 495 virtual ~ZNames();
michael@0 496
michael@0 497 static ZNames* createInstance(UResourceBundle* rb, const char* key);
michael@0 498 virtual const UChar* getName(UTimeZoneNameType type);
michael@0 499
michael@0 500 protected:
michael@0 501 ZNames(const UChar** names);
michael@0 502 static const UChar** loadData(UResourceBundle* rb, const char* key);
michael@0 503
michael@0 504 private:
michael@0 505 const UChar** fNames;
michael@0 506 };
michael@0 507
michael@0 508 ZNames::ZNames(const UChar** names)
michael@0 509 : fNames(names) {
michael@0 510 }
michael@0 511
michael@0 512 ZNames::~ZNames() {
michael@0 513 if (fNames != NULL) {
michael@0 514 uprv_free(fNames);
michael@0 515 }
michael@0 516 }
michael@0 517
michael@0 518 ZNames*
michael@0 519 ZNames::createInstance(UResourceBundle* rb, const char* key) {
michael@0 520 const UChar** names = loadData(rb, key);
michael@0 521 if (names == NULL) {
michael@0 522 // No names data available
michael@0 523 return NULL;
michael@0 524 }
michael@0 525 return new ZNames(names);
michael@0 526 }
michael@0 527
michael@0 528 const UChar*
michael@0 529 ZNames::getName(UTimeZoneNameType type) {
michael@0 530 if (fNames == NULL) {
michael@0 531 return NULL;
michael@0 532 }
michael@0 533 const UChar *name = NULL;
michael@0 534 switch(type) {
michael@0 535 case UTZNM_LONG_GENERIC:
michael@0 536 name = fNames[0];
michael@0 537 break;
michael@0 538 case UTZNM_LONG_STANDARD:
michael@0 539 name = fNames[1];
michael@0 540 break;
michael@0 541 case UTZNM_LONG_DAYLIGHT:
michael@0 542 name = fNames[2];
michael@0 543 break;
michael@0 544 case UTZNM_SHORT_GENERIC:
michael@0 545 name = fNames[3];
michael@0 546 break;
michael@0 547 case UTZNM_SHORT_STANDARD:
michael@0 548 name = fNames[4];
michael@0 549 break;
michael@0 550 case UTZNM_SHORT_DAYLIGHT:
michael@0 551 name = fNames[5];
michael@0 552 break;
michael@0 553 case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass
michael@0 554 default:
michael@0 555 name = NULL;
michael@0 556 }
michael@0 557 return name;
michael@0 558 }
michael@0 559
michael@0 560 const UChar**
michael@0 561 ZNames::loadData(UResourceBundle* rb, const char* key) {
michael@0 562 if (rb == NULL || key == NULL || *key == 0) {
michael@0 563 return NULL;
michael@0 564 }
michael@0 565
michael@0 566 UErrorCode status = U_ZERO_ERROR;
michael@0 567 const UChar **names = NULL;
michael@0 568
michael@0 569 UResourceBundle* rbTable = NULL;
michael@0 570 rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status);
michael@0 571 if (U_SUCCESS(status)) {
michael@0 572 names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE);
michael@0 573 if (names != NULL) {
michael@0 574 UBool isEmpty = TRUE;
michael@0 575 for (int32_t i = 0; i < KEYS_SIZE; i++) {
michael@0 576 status = U_ZERO_ERROR;
michael@0 577 int32_t len = 0;
michael@0 578 const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status);
michael@0 579 if (U_FAILURE(status) || len == 0) {
michael@0 580 names[i] = NULL;
michael@0 581 } else {
michael@0 582 names[i] = value;
michael@0 583 isEmpty = FALSE;
michael@0 584 }
michael@0 585 }
michael@0 586 if (isEmpty) {
michael@0 587 // No need to keep the names array
michael@0 588 uprv_free(names);
michael@0 589 names = NULL;
michael@0 590 }
michael@0 591 }
michael@0 592 }
michael@0 593 ures_close(rbTable);
michael@0 594 return names;
michael@0 595 }
michael@0 596
michael@0 597 // ---------------------------------------------------
michael@0 598 // TZNames - names for a time zone
michael@0 599 // ---------------------------------------------------
michael@0 600 class TZNames : public ZNames {
michael@0 601 public:
michael@0 602 virtual ~TZNames();
michael@0 603
michael@0 604 static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID);
michael@0 605 virtual const UChar* getName(UTimeZoneNameType type);
michael@0 606
michael@0 607 private:
michael@0 608 TZNames(const UChar** names);
michael@0 609 const UChar* fLocationName;
michael@0 610 UChar* fLocationNameOwned;
michael@0 611 };
michael@0 612
michael@0 613 TZNames::TZNames(const UChar** names)
michael@0 614 : ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) {
michael@0 615 }
michael@0 616
michael@0 617 TZNames::~TZNames() {
michael@0 618 if (fLocationNameOwned) {
michael@0 619 uprv_free(fLocationNameOwned);
michael@0 620 }
michael@0 621 }
michael@0 622
michael@0 623 const UChar*
michael@0 624 TZNames::getName(UTimeZoneNameType type) {
michael@0 625 if (type == UTZNM_EXEMPLAR_LOCATION) {
michael@0 626 return fLocationName;
michael@0 627 }
michael@0 628 return ZNames::getName(type);
michael@0 629 }
michael@0 630
michael@0 631 TZNames*
michael@0 632 TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) {
michael@0 633 if (rb == NULL || key == NULL || *key == 0) {
michael@0 634 return NULL;
michael@0 635 }
michael@0 636
michael@0 637 const UChar** names = loadData(rb, key);
michael@0 638 const UChar* locationName = NULL;
michael@0 639 UChar* locationNameOwned = NULL;
michael@0 640
michael@0 641 UErrorCode status = U_ZERO_ERROR;
michael@0 642 int32_t len = 0;
michael@0 643
michael@0 644 UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status);
michael@0 645 locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status);
michael@0 646 // ignore missing resource here
michael@0 647 status = U_ZERO_ERROR;
michael@0 648
michael@0 649 ures_close(table);
michael@0 650
michael@0 651 if (locationName == NULL) {
michael@0 652 UnicodeString tmpName;
michael@0 653 int32_t tmpNameLen = 0;
michael@0 654 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName);
michael@0 655 tmpNameLen = tmpName.length();
michael@0 656
michael@0 657 if (tmpNameLen > 0) {
michael@0 658 locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1));
michael@0 659 if (locationNameOwned) {
michael@0 660 tmpName.extract(locationNameOwned, tmpNameLen + 1, status);
michael@0 661 locationName = locationNameOwned;
michael@0 662 }
michael@0 663 }
michael@0 664 }
michael@0 665
michael@0 666 TZNames* tznames = NULL;
michael@0 667 if (locationName != NULL || names != NULL) {
michael@0 668 tznames = new TZNames(names);
michael@0 669 if (tznames == NULL) {
michael@0 670 if (locationNameOwned) {
michael@0 671 uprv_free(locationNameOwned);
michael@0 672 }
michael@0 673 }
michael@0 674 tznames->fLocationName = locationName;
michael@0 675 tznames->fLocationNameOwned = locationNameOwned;
michael@0 676 }
michael@0 677
michael@0 678 return tznames;
michael@0 679 }
michael@0 680
michael@0 681 // ---------------------------------------------------
michael@0 682 // The meta zone ID enumeration class
michael@0 683 // ---------------------------------------------------
michael@0 684 class MetaZoneIDsEnumeration : public StringEnumeration {
michael@0 685 public:
michael@0 686 MetaZoneIDsEnumeration();
michael@0 687 MetaZoneIDsEnumeration(const UVector& mzIDs);
michael@0 688 MetaZoneIDsEnumeration(UVector* mzIDs);
michael@0 689 virtual ~MetaZoneIDsEnumeration();
michael@0 690 static UClassID U_EXPORT2 getStaticClassID(void);
michael@0 691 virtual UClassID getDynamicClassID(void) const;
michael@0 692 virtual const UnicodeString* snext(UErrorCode& status);
michael@0 693 virtual void reset(UErrorCode& status);
michael@0 694 virtual int32_t count(UErrorCode& status) const;
michael@0 695 private:
michael@0 696 int32_t fLen;
michael@0 697 int32_t fPos;
michael@0 698 const UVector* fMetaZoneIDs;
michael@0 699 UVector *fLocalVector;
michael@0 700 };
michael@0 701
michael@0 702 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
michael@0 703
michael@0 704 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
michael@0 705 : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
michael@0 706 }
michael@0 707
michael@0 708 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
michael@0 709 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
michael@0 710 fLen = fMetaZoneIDs->size();
michael@0 711 }
michael@0 712
michael@0 713 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
michael@0 714 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
michael@0 715 if (fMetaZoneIDs) {
michael@0 716 fLen = fMetaZoneIDs->size();
michael@0 717 }
michael@0 718 }
michael@0 719
michael@0 720 const UnicodeString*
michael@0 721 MetaZoneIDsEnumeration::snext(UErrorCode& status) {
michael@0 722 if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
michael@0 723 unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
michael@0 724 return &unistr;
michael@0 725 }
michael@0 726 return NULL;
michael@0 727 }
michael@0 728
michael@0 729 void
michael@0 730 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
michael@0 731 fPos = 0;
michael@0 732 }
michael@0 733
michael@0 734 int32_t
michael@0 735 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
michael@0 736 return fLen;
michael@0 737 }
michael@0 738
michael@0 739 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
michael@0 740 if (fLocalVector) {
michael@0 741 delete fLocalVector;
michael@0 742 }
michael@0 743 }
michael@0 744
michael@0 745 U_CDECL_BEGIN
michael@0 746 /**
michael@0 747 * ZNameInfo stores zone name information in the trie
michael@0 748 */
michael@0 749 typedef struct ZNameInfo {
michael@0 750 UTimeZoneNameType type;
michael@0 751 const UChar* tzID;
michael@0 752 const UChar* mzID;
michael@0 753 } ZNameInfo;
michael@0 754
michael@0 755 /**
michael@0 756 * ZMatchInfo stores zone name match information used by find method
michael@0 757 */
michael@0 758 typedef struct ZMatchInfo {
michael@0 759 const ZNameInfo* znameInfo;
michael@0 760 int32_t matchLength;
michael@0 761 } ZMatchInfo;
michael@0 762 U_CDECL_END
michael@0 763
michael@0 764
michael@0 765 // ---------------------------------------------------
michael@0 766 // ZNameSearchHandler
michael@0 767 // ---------------------------------------------------
michael@0 768 class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
michael@0 769 public:
michael@0 770 ZNameSearchHandler(uint32_t types);
michael@0 771 virtual ~ZNameSearchHandler();
michael@0 772
michael@0 773 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
michael@0 774 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
michael@0 775
michael@0 776 private:
michael@0 777 uint32_t fTypes;
michael@0 778 int32_t fMaxMatchLen;
michael@0 779 TimeZoneNames::MatchInfoCollection* fResults;
michael@0 780 };
michael@0 781
michael@0 782 ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
michael@0 783 : fTypes(types), fMaxMatchLen(0), fResults(NULL) {
michael@0 784 }
michael@0 785
michael@0 786 ZNameSearchHandler::~ZNameSearchHandler() {
michael@0 787 if (fResults != NULL) {
michael@0 788 delete fResults;
michael@0 789 }
michael@0 790 }
michael@0 791
michael@0 792 UBool
michael@0 793 ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
michael@0 794 if (U_FAILURE(status)) {
michael@0 795 return FALSE;
michael@0 796 }
michael@0 797 if (node->hasValues()) {
michael@0 798 int32_t valuesCount = node->countValues();
michael@0 799 for (int32_t i = 0; i < valuesCount; i++) {
michael@0 800 ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
michael@0 801 if (nameinfo == NULL) {
michael@0 802 break;
michael@0 803 }
michael@0 804 if ((nameinfo->type & fTypes) != 0) {
michael@0 805 // matches a requested type
michael@0 806 if (fResults == NULL) {
michael@0 807 fResults = new TimeZoneNames::MatchInfoCollection();
michael@0 808 if (fResults == NULL) {
michael@0 809 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 810 }
michael@0 811 }
michael@0 812 if (U_SUCCESS(status)) {
michael@0 813 U_ASSERT(fResults != NULL);
michael@0 814 if (nameinfo->tzID) {
michael@0 815 fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
michael@0 816 } else {
michael@0 817 U_ASSERT(nameinfo->mzID);
michael@0 818 fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
michael@0 819 }
michael@0 820 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
michael@0 821 fMaxMatchLen = matchLength;
michael@0 822 }
michael@0 823 }
michael@0 824 }
michael@0 825 }
michael@0 826 }
michael@0 827 return TRUE;
michael@0 828 }
michael@0 829
michael@0 830 TimeZoneNames::MatchInfoCollection*
michael@0 831 ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
michael@0 832 // give the ownership to the caller
michael@0 833 TimeZoneNames::MatchInfoCollection* results = fResults;
michael@0 834 maxMatchLen = fMaxMatchLen;
michael@0 835
michael@0 836 // reset
michael@0 837 fResults = NULL;
michael@0 838 fMaxMatchLen = 0;
michael@0 839 return results;
michael@0 840 }
michael@0 841
michael@0 842 // ---------------------------------------------------
michael@0 843 // TimeZoneNamesImpl
michael@0 844 //
michael@0 845 // TimeZoneNames implementation class. This is the main
michael@0 846 // part of this module.
michael@0 847 // ---------------------------------------------------
michael@0 848
michael@0 849 U_CDECL_BEGIN
michael@0 850 /**
michael@0 851 * Deleter for ZNames
michael@0 852 */
michael@0 853 static void U_CALLCONV
michael@0 854 deleteZNames(void *obj) {
michael@0 855 if (obj != EMPTY) {
michael@0 856 delete (ZNames *)obj;
michael@0 857 }
michael@0 858 }
michael@0 859 /**
michael@0 860 * Deleter for TZNames
michael@0 861 */
michael@0 862 static void U_CALLCONV
michael@0 863 deleteTZNames(void *obj) {
michael@0 864 if (obj != EMPTY) {
michael@0 865 delete (TZNames *)obj;
michael@0 866 }
michael@0 867 }
michael@0 868
michael@0 869 /**
michael@0 870 * Deleter for ZNameInfo
michael@0 871 */
michael@0 872 static void U_CALLCONV
michael@0 873 deleteZNameInfo(void *obj) {
michael@0 874 uprv_free(obj);
michael@0 875 }
michael@0 876
michael@0 877 U_CDECL_END
michael@0 878
michael@0 879 static UMutex gLock = U_MUTEX_INITIALIZER;
michael@0 880
michael@0 881 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
michael@0 882 : fLocale(locale),
michael@0 883 fZoneStrings(NULL),
michael@0 884 fTZNamesMap(NULL),
michael@0 885 fMZNamesMap(NULL),
michael@0 886 fNamesTrieFullyLoaded(FALSE),
michael@0 887 fNamesTrie(TRUE, deleteZNameInfo) {
michael@0 888 initialize(locale, status);
michael@0 889 }
michael@0 890
michael@0 891 void
michael@0 892 TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
michael@0 893 if (U_FAILURE(status)) {
michael@0 894 return;
michael@0 895 }
michael@0 896
michael@0 897 // Load zoneStrings bundle
michael@0 898 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
michael@0 899 fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
michael@0 900 fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
michael@0 901 if (U_FAILURE(tmpsts)) {
michael@0 902 status = tmpsts;
michael@0 903 cleanup();
michael@0 904 return;
michael@0 905 }
michael@0 906
michael@0 907 // Initialize hashtables holding time zone/meta zone names
michael@0 908 fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
michael@0 909 fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
michael@0 910 if (U_FAILURE(status)) {
michael@0 911 cleanup();
michael@0 912 return;
michael@0 913 }
michael@0 914
michael@0 915 uhash_setValueDeleter(fMZNamesMap, deleteZNames);
michael@0 916 uhash_setValueDeleter(fTZNamesMap, deleteTZNames);
michael@0 917 // no key deleters for name maps
michael@0 918
michael@0 919 // preload zone strings for the default zone
michael@0 920 TimeZone *tz = TimeZone::createDefault();
michael@0 921 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
michael@0 922 if (tzID != NULL) {
michael@0 923 loadStrings(UnicodeString(tzID));
michael@0 924 }
michael@0 925 delete tz;
michael@0 926
michael@0 927 return;
michael@0 928 }
michael@0 929
michael@0 930 /*
michael@0 931 * This method updates the cache and must be called with a lock,
michael@0 932 * except initializer.
michael@0 933 */
michael@0 934 void
michael@0 935 TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
michael@0 936 loadTimeZoneNames(tzCanonicalID);
michael@0 937
michael@0 938 UErrorCode status = U_ZERO_ERROR;
michael@0 939 StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status);
michael@0 940 if (U_SUCCESS(status) && mzIDs != NULL) {
michael@0 941 const UnicodeString *mzID;
michael@0 942 while ((mzID = mzIDs->snext(status))) {
michael@0 943 if (U_FAILURE(status)) {
michael@0 944 break;
michael@0 945 }
michael@0 946 loadMetaZoneNames(*mzID);
michael@0 947 }
michael@0 948 delete mzIDs;
michael@0 949 }
michael@0 950 }
michael@0 951
michael@0 952 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
michael@0 953 cleanup();
michael@0 954 }
michael@0 955
michael@0 956 void
michael@0 957 TimeZoneNamesImpl::cleanup() {
michael@0 958 if (fZoneStrings != NULL) {
michael@0 959 ures_close(fZoneStrings);
michael@0 960 fZoneStrings = NULL;
michael@0 961 }
michael@0 962 if (fMZNamesMap != NULL) {
michael@0 963 uhash_close(fMZNamesMap);
michael@0 964 fMZNamesMap = NULL;
michael@0 965 }
michael@0 966 if (fTZNamesMap != NULL) {
michael@0 967 uhash_close(fTZNamesMap);
michael@0 968 fTZNamesMap = NULL;
michael@0 969 }
michael@0 970 }
michael@0 971
michael@0 972 UBool
michael@0 973 TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
michael@0 974 if (this == &other) {
michael@0 975 return TRUE;
michael@0 976 }
michael@0 977 // No implementation for now
michael@0 978 return FALSE;
michael@0 979 }
michael@0 980
michael@0 981 TimeZoneNames*
michael@0 982 TimeZoneNamesImpl::clone() const {
michael@0 983 UErrorCode status = U_ZERO_ERROR;
michael@0 984 return new TimeZoneNamesImpl(fLocale, status);
michael@0 985 }
michael@0 986
michael@0 987 StringEnumeration*
michael@0 988 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
michael@0 989 if (U_FAILURE(status)) {
michael@0 990 return NULL;
michael@0 991 }
michael@0 992 const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
michael@0 993 if (mzIDs == NULL) {
michael@0 994 return new MetaZoneIDsEnumeration();
michael@0 995 }
michael@0 996 return new MetaZoneIDsEnumeration(*mzIDs);
michael@0 997 }
michael@0 998
michael@0 999 StringEnumeration*
michael@0 1000 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
michael@0 1001 if (U_FAILURE(status)) {
michael@0 1002 return NULL;
michael@0 1003 }
michael@0 1004 const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
michael@0 1005 if (mappings == NULL) {
michael@0 1006 return new MetaZoneIDsEnumeration();
michael@0 1007 }
michael@0 1008
michael@0 1009 MetaZoneIDsEnumeration *senum = NULL;
michael@0 1010 UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
michael@0 1011 if (mzIDs == NULL) {
michael@0 1012 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1013 }
michael@0 1014 if (U_SUCCESS(status)) {
michael@0 1015 U_ASSERT(mzIDs != NULL);
michael@0 1016 for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
michael@0 1017
michael@0 1018 OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
michael@0 1019 const UChar *mzID = map->mzid;
michael@0 1020 if (!mzIDs->contains((void *)mzID)) {
michael@0 1021 mzIDs->addElement((void *)mzID, status);
michael@0 1022 }
michael@0 1023 }
michael@0 1024 if (U_SUCCESS(status)) {
michael@0 1025 senum = new MetaZoneIDsEnumeration(mzIDs);
michael@0 1026 } else {
michael@0 1027 delete mzIDs;
michael@0 1028 }
michael@0 1029 }
michael@0 1030 return senum;
michael@0 1031 }
michael@0 1032
michael@0 1033 UnicodeString&
michael@0 1034 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
michael@0 1035 ZoneMeta::getMetazoneID(tzID, date, mzID);
michael@0 1036 return mzID;
michael@0 1037 }
michael@0 1038
michael@0 1039 UnicodeString&
michael@0 1040 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
michael@0 1041 ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
michael@0 1042 return tzID;
michael@0 1043 }
michael@0 1044
michael@0 1045 UnicodeString&
michael@0 1046 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
michael@0 1047 UTimeZoneNameType type,
michael@0 1048 UnicodeString& name) const {
michael@0 1049 name.setToBogus(); // cleanup result.
michael@0 1050 if (mzID.isEmpty()) {
michael@0 1051 return name;
michael@0 1052 }
michael@0 1053
michael@0 1054 ZNames *znames = NULL;
michael@0 1055 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
michael@0 1056
michael@0 1057 umtx_lock(&gLock);
michael@0 1058 {
michael@0 1059 znames = nonConstThis->loadMetaZoneNames(mzID);
michael@0 1060 }
michael@0 1061 umtx_unlock(&gLock);
michael@0 1062
michael@0 1063 if (znames != NULL) {
michael@0 1064 const UChar* s = znames->getName(type);
michael@0 1065 if (s != NULL) {
michael@0 1066 name.setTo(TRUE, s, -1);
michael@0 1067 }
michael@0 1068 }
michael@0 1069 return name;
michael@0 1070 }
michael@0 1071
michael@0 1072 UnicodeString&
michael@0 1073 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
michael@0 1074 name.setToBogus(); // cleanup result.
michael@0 1075 if (tzID.isEmpty()) {
michael@0 1076 return name;
michael@0 1077 }
michael@0 1078
michael@0 1079 TZNames *tznames = NULL;
michael@0 1080 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
michael@0 1081
michael@0 1082 umtx_lock(&gLock);
michael@0 1083 {
michael@0 1084 tznames = nonConstThis->loadTimeZoneNames(tzID);
michael@0 1085 }
michael@0 1086 umtx_unlock(&gLock);
michael@0 1087
michael@0 1088 if (tznames != NULL) {
michael@0 1089 const UChar *s = tznames->getName(type);
michael@0 1090 if (s != NULL) {
michael@0 1091 name.setTo(TRUE, s, -1);
michael@0 1092 }
michael@0 1093 }
michael@0 1094 return name;
michael@0 1095 }
michael@0 1096
michael@0 1097 UnicodeString&
michael@0 1098 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
michael@0 1099 name.setToBogus(); // cleanup result.
michael@0 1100 const UChar* locName = NULL;
michael@0 1101 TZNames *tznames = NULL;
michael@0 1102 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
michael@0 1103
michael@0 1104 umtx_lock(&gLock);
michael@0 1105 {
michael@0 1106 tznames = nonConstThis->loadTimeZoneNames(tzID);
michael@0 1107 }
michael@0 1108 umtx_unlock(&gLock);
michael@0 1109
michael@0 1110 if (tznames != NULL) {
michael@0 1111 locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
michael@0 1112 }
michael@0 1113 if (locName != NULL) {
michael@0 1114 name.setTo(TRUE, locName, -1);
michael@0 1115 }
michael@0 1116
michael@0 1117 return name;
michael@0 1118 }
michael@0 1119
michael@0 1120
michael@0 1121 // Merge the MZ_PREFIX and mzId
michael@0 1122 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
michael@0 1123 if (mzID.isEmpty()) {
michael@0 1124 result[0] = '\0';
michael@0 1125 return;
michael@0 1126 }
michael@0 1127
michael@0 1128 char mzIdChar[ZID_KEY_MAX + 1];
michael@0 1129 int32_t keyLen;
michael@0 1130 int32_t prefixLen = uprv_strlen(gMZPrefix);
michael@0 1131 keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
michael@0 1132 uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
michael@0 1133 uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
michael@0 1134 result[keyLen + prefixLen] = '\0';
michael@0 1135 }
michael@0 1136
michael@0 1137 /*
michael@0 1138 * This method updates the cache and must be called with a lock
michael@0 1139 */
michael@0 1140 ZNames*
michael@0 1141 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) {
michael@0 1142 if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) {
michael@0 1143 return NULL;
michael@0 1144 }
michael@0 1145
michael@0 1146 ZNames *znames = NULL;
michael@0 1147
michael@0 1148 UErrorCode status = U_ZERO_ERROR;
michael@0 1149 UChar mzIDKey[ZID_KEY_MAX + 1];
michael@0 1150 mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
michael@0 1151 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
michael@0 1152 mzIDKey[mzID.length()] = 0;
michael@0 1153
michael@0 1154 void *cacheVal = uhash_get(fMZNamesMap, mzIDKey);
michael@0 1155 if (cacheVal == NULL) {
michael@0 1156 char key[ZID_KEY_MAX + 1];
michael@0 1157 mergeTimeZoneKey(mzID, key);
michael@0 1158 znames = ZNames::createInstance(fZoneStrings, key);
michael@0 1159
michael@0 1160 if (znames == NULL) {
michael@0 1161 cacheVal = (void *)EMPTY;
michael@0 1162 } else {
michael@0 1163 cacheVal = znames;
michael@0 1164 }
michael@0 1165 // Use the persistent ID as the resource key, so we can
michael@0 1166 // avoid duplications.
michael@0 1167 const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
michael@0 1168 if (newKey != NULL) {
michael@0 1169 uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status);
michael@0 1170 if (U_FAILURE(status)) {
michael@0 1171 if (znames != NULL) {
michael@0 1172 delete znames;
michael@0 1173 }
michael@0 1174 } else if (znames != NULL) {
michael@0 1175 // put the name info into the trie
michael@0 1176 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
michael@0 1177 const UChar* name = znames->getName(ALL_NAME_TYPES[i]);
michael@0 1178 if (name != NULL) {
michael@0 1179 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
michael@0 1180 if (nameinfo != NULL) {
michael@0 1181 nameinfo->type = ALL_NAME_TYPES[i];
michael@0 1182 nameinfo->tzID = NULL;
michael@0 1183 nameinfo->mzID = newKey;
michael@0 1184 fNamesTrie.put(name, nameinfo, status);
michael@0 1185 }
michael@0 1186 }
michael@0 1187 }
michael@0 1188 }
michael@0 1189
michael@0 1190 } else {
michael@0 1191 // Should never happen with a valid input
michael@0 1192 if (znames != NULL) {
michael@0 1193 // It's not possible that we get a valid ZNames with unknown ID.
michael@0 1194 // But just in case..
michael@0 1195 delete znames;
michael@0 1196 znames = NULL;
michael@0 1197 }
michael@0 1198 }
michael@0 1199 } else if (cacheVal != EMPTY) {
michael@0 1200 znames = (ZNames *)cacheVal;
michael@0 1201 }
michael@0 1202
michael@0 1203 return znames;
michael@0 1204 }
michael@0 1205
michael@0 1206 /*
michael@0 1207 * This method updates the cache and must be called with a lock
michael@0 1208 */
michael@0 1209 TZNames*
michael@0 1210 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
michael@0 1211 if (tzID.length() > ZID_KEY_MAX) {
michael@0 1212 return NULL;
michael@0 1213 }
michael@0 1214
michael@0 1215 TZNames *tznames = NULL;
michael@0 1216
michael@0 1217 UErrorCode status = U_ZERO_ERROR;
michael@0 1218 UChar tzIDKey[ZID_KEY_MAX + 1];
michael@0 1219 int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
michael@0 1220 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
michael@0 1221 tzIDKey[tzIDKeyLen] = 0;
michael@0 1222
michael@0 1223 void *cacheVal = uhash_get(fTZNamesMap, tzIDKey);
michael@0 1224 if (cacheVal == NULL) {
michael@0 1225 char key[ZID_KEY_MAX + 1];
michael@0 1226 UErrorCode status = U_ZERO_ERROR;
michael@0 1227 // Replace "/" with ":".
michael@0 1228 UnicodeString uKey(tzID);
michael@0 1229 for (int32_t i = 0; i < uKey.length(); i++) {
michael@0 1230 if (uKey.charAt(i) == (UChar)0x2F) {
michael@0 1231 uKey.setCharAt(i, (UChar)0x3A);
michael@0 1232 }
michael@0 1233 }
michael@0 1234 uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
michael@0 1235 tznames = TZNames::createInstance(fZoneStrings, key, tzID);
michael@0 1236
michael@0 1237 if (tznames == NULL) {
michael@0 1238 cacheVal = (void *)EMPTY;
michael@0 1239 } else {
michael@0 1240 cacheVal = tznames;
michael@0 1241 }
michael@0 1242 // Use the persistent ID as the resource key, so we can
michael@0 1243 // avoid duplications.
michael@0 1244 const UChar* newKey = ZoneMeta::findTimeZoneID(tzID);
michael@0 1245 if (newKey != NULL) {
michael@0 1246 uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status);
michael@0 1247 if (U_FAILURE(status)) {
michael@0 1248 if (tznames != NULL) {
michael@0 1249 delete tznames;
michael@0 1250 }
michael@0 1251 } else if (tznames != NULL) {
michael@0 1252 // put the name info into the trie
michael@0 1253 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
michael@0 1254 const UChar* name = tznames->getName(ALL_NAME_TYPES[i]);
michael@0 1255 if (name != NULL) {
michael@0 1256 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
michael@0 1257 if (nameinfo != NULL) {
michael@0 1258 nameinfo->type = ALL_NAME_TYPES[i];
michael@0 1259 nameinfo->tzID = newKey;
michael@0 1260 nameinfo->mzID = NULL;
michael@0 1261 fNamesTrie.put(name, nameinfo, status);
michael@0 1262 }
michael@0 1263 }
michael@0 1264 }
michael@0 1265 }
michael@0 1266 } else {
michael@0 1267 // Should never happen with a valid input
michael@0 1268 if (tznames != NULL) {
michael@0 1269 // It's not possible that we get a valid TZNames with unknown ID.
michael@0 1270 // But just in case..
michael@0 1271 delete tznames;
michael@0 1272 tznames = NULL;
michael@0 1273 }
michael@0 1274 }
michael@0 1275 } else if (cacheVal != EMPTY) {
michael@0 1276 tznames = (TZNames *)cacheVal;
michael@0 1277 }
michael@0 1278
michael@0 1279 return tznames;
michael@0 1280 }
michael@0 1281
michael@0 1282 TimeZoneNames::MatchInfoCollection*
michael@0 1283 TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
michael@0 1284 ZNameSearchHandler handler(types);
michael@0 1285
michael@0 1286 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
michael@0 1287
michael@0 1288 umtx_lock(&gLock);
michael@0 1289 {
michael@0 1290 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
michael@0 1291 }
michael@0 1292 umtx_unlock(&gLock);
michael@0 1293
michael@0 1294 if (U_FAILURE(status)) {
michael@0 1295 return NULL;
michael@0 1296 }
michael@0 1297
michael@0 1298 int32_t maxLen = 0;
michael@0 1299 TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
michael@0 1300 if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
michael@0 1301 // perfect match
michael@0 1302 return matches;
michael@0 1303 }
michael@0 1304
michael@0 1305 delete matches;
michael@0 1306
michael@0 1307 // All names are not yet loaded into the trie
michael@0 1308 umtx_lock(&gLock);
michael@0 1309 {
michael@0 1310 if (!fNamesTrieFullyLoaded) {
michael@0 1311 const UnicodeString *id;
michael@0 1312
michael@0 1313 // load strings for all zones
michael@0 1314 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
michael@0 1315 if (U_SUCCESS(status)) {
michael@0 1316 while ((id = tzIDs->snext(status))) {
michael@0 1317 if (U_FAILURE(status)) {
michael@0 1318 break;
michael@0 1319 }
michael@0 1320 // loadStrings also load related metazone strings
michael@0 1321 nonConstThis->loadStrings(*id);
michael@0 1322 }
michael@0 1323 }
michael@0 1324 if (tzIDs != NULL) {
michael@0 1325 delete tzIDs;
michael@0 1326 }
michael@0 1327 if (U_SUCCESS(status)) {
michael@0 1328 nonConstThis->fNamesTrieFullyLoaded = TRUE;
michael@0 1329 }
michael@0 1330 }
michael@0 1331 }
michael@0 1332 umtx_unlock(&gLock);
michael@0 1333
michael@0 1334 if (U_FAILURE(status)) {
michael@0 1335 return NULL;
michael@0 1336 }
michael@0 1337
michael@0 1338 umtx_lock(&gLock);
michael@0 1339 {
michael@0 1340 // now try it again
michael@0 1341 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
michael@0 1342 }
michael@0 1343 umtx_unlock(&gLock);
michael@0 1344
michael@0 1345 return handler.getMatches(maxLen);
michael@0 1346 }
michael@0 1347
michael@0 1348 static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
michael@0 1349 static const int32_t gEtcPrefixLen = 4;
michael@0 1350 static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
michael@0 1351 static const int32_t gSystemVPrefixLen = 8;
michael@0 1352 static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
michael@0 1353 static const int32_t gRiyadh8Len = 7;
michael@0 1354
michael@0 1355 UnicodeString& U_EXPORT2
michael@0 1356 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
michael@0 1357 if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
michael@0 1358 || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
michael@0 1359 name.setToBogus();
michael@0 1360 return name;
michael@0 1361 }
michael@0 1362
michael@0 1363 int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
michael@0 1364 if (sep > 0 && sep + 1 < tzID.length()) {
michael@0 1365 name.setTo(tzID, sep + 1);
michael@0 1366 name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
michael@0 1367 UnicodeString((UChar)0x20 /* space */));
michael@0 1368 } else {
michael@0 1369 name.setToBogus();
michael@0 1370 }
michael@0 1371 return name;
michael@0 1372 }
michael@0 1373
michael@0 1374 U_NAMESPACE_END
michael@0 1375
michael@0 1376
michael@0 1377 #endif /* #if !UCONFIG_NO_FORMATTING */
michael@0 1378
michael@0 1379 //eof

mercurial