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