intl/icu/source/i18n/tznames_impl.cpp

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

mercurial