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