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