2308 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2308 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 2016 and later: Unicode, Inc. and others.
 | |
| // License & terms of use: http://www.unicode.org/copyright.html
 | |
| /*
 | |
| *******************************************************************************
 | |
| * Copyright (C) 2011-2016, International Business Machines Corporation and
 | |
| * others. All Rights Reserved.
 | |
| *******************************************************************************
 | |
| *
 | |
| * File TZNAMES_IMPL.CPP
 | |
| *
 | |
| *******************************************************************************
 | |
| */
 | |
| 
 | |
| #include "unicode/utypes.h"
 | |
| 
 | |
| #if !UCONFIG_NO_FORMATTING
 | |
| 
 | |
| #include "unicode/strenum.h"
 | |
| #include "unicode/ustring.h"
 | |
| #include "unicode/timezone.h"
 | |
| #include "unicode/utf16.h"
 | |
| 
 | |
| #include "tznames_impl.h"
 | |
| #include "bytesinkutil.h"
 | |
| #include "charstr.h"
 | |
| #include "cmemory.h"
 | |
| #include "cstring.h"
 | |
| #include "uassert.h"
 | |
| #include "mutex.h"
 | |
| #include "resource.h"
 | |
| #include "ulocimp.h"
 | |
| #include "uresimp.h"
 | |
| #include "ureslocs.h"
 | |
| #include "zonemeta.h"
 | |
| #include "ucln_in.h"
 | |
| #include "uvector.h"
 | |
| #include "olsontz.h"
 | |
| 
 | |
| U_NAMESPACE_BEGIN
 | |
| 
 | |
| #define ZID_KEY_MAX  128
 | |
| #define MZ_PREFIX_LEN 5
 | |
| 
 | |
| static const char gZoneStrings[]        = "zoneStrings";
 | |
| static const char gMZPrefix[]           = "meta:";
 | |
| 
 | |
| static const char EMPTY[]               = "<empty>";   // place holder for empty ZNames
 | |
| static const char DUMMY_LOADER[]        = "<dummy>";   // place holder for dummy ZNamesLoader
 | |
| static const UChar NO_NAME[]            = { 0 };   // for empty no-fallback time zone names
 | |
| 
 | |
| // stuff for TZDBTimeZoneNames
 | |
| static const char* TZDBNAMES_KEYS[]               = {"ss", "sd"};
 | |
| static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
 | |
| 
 | |
| static UMutex gDataMutex;
 | |
| 
 | |
| static UHashtable* gTZDBNamesMap = NULL;
 | |
| static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
 | |
| 
 | |
| static TextTrieMap* gTZDBNamesTrie = NULL;
 | |
| static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER;
 | |
| 
 | |
| // The order in which strings are stored may be different than the order in the public enum.
 | |
| enum UTimeZoneNameTypeIndex {
 | |
|     UTZNM_INDEX_UNKNOWN = -1,
 | |
|     UTZNM_INDEX_EXEMPLAR_LOCATION,
 | |
|     UTZNM_INDEX_LONG_GENERIC,
 | |
|     UTZNM_INDEX_LONG_STANDARD,
 | |
|     UTZNM_INDEX_LONG_DAYLIGHT,
 | |
|     UTZNM_INDEX_SHORT_GENERIC,
 | |
|     UTZNM_INDEX_SHORT_STANDARD,
 | |
|     UTZNM_INDEX_SHORT_DAYLIGHT,
 | |
|     UTZNM_INDEX_COUNT
 | |
| };
 | |
| static const UChar* const EMPTY_NAMES[UTZNM_INDEX_COUNT] = {0,0,0,0,0,0,0};
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) {
 | |
|     if (gTZDBNamesMap != NULL) {
 | |
|         uhash_close(gTZDBNamesMap);
 | |
|         gTZDBNamesMap = NULL;
 | |
|     }
 | |
|     gTZDBNamesMapInitOnce.reset();
 | |
| 
 | |
|     if (gTZDBNamesTrie != NULL) {
 | |
|         delete gTZDBNamesTrie;
 | |
|         gTZDBNamesTrie = NULL;
 | |
|     }
 | |
|     gTZDBNamesTrieInitOnce.reset();
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| U_CDECL_END
 | |
| 
 | |
| /**
 | |
|  * ZNameInfo stores zone name information in the trie
 | |
|  */
 | |
| struct ZNameInfo {
 | |
|     UTimeZoneNameType   type;
 | |
|     const UChar*        tzID;
 | |
|     const UChar*        mzID;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * ZMatchInfo stores zone name match information used by find method
 | |
|  */
 | |
| struct ZMatchInfo {
 | |
|     const ZNameInfo*    znameInfo;
 | |
|     int32_t             matchLength;
 | |
| };
 | |
| 
 | |
| // Helper functions
 | |
| static void mergeTimeZoneKey(const UnicodeString& mzID, char* result);
 | |
| 
 | |
| #define DEFAULT_CHARACTERNODE_CAPACITY 1
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // CharacterNode class implementation
 | |
| // ---------------------------------------------------
 | |
| void CharacterNode::clear() {
 | |
|     uprv_memset(this, 0, sizeof(*this));
 | |
| }
 | |
| 
 | |
| void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
 | |
|     if (fValues == NULL) {
 | |
|         // Do nothing.
 | |
|     } else if (!fHasValuesVector) {
 | |
|         if (valueDeleter) {
 | |
|             valueDeleter(fValues);
 | |
|         }
 | |
|     } else {
 | |
|         delete (UVector *)fValues;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         if (valueDeleter) {
 | |
|             valueDeleter(value);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if (fValues == NULL) {
 | |
|         fValues = value;
 | |
|     } else {
 | |
|         // At least one value already.
 | |
|         if (!fHasValuesVector) {
 | |
|             // There is only one value so far, and not in a vector yet.
 | |
|             // Create a vector and add the old value.
 | |
|             UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
 | |
|             if (U_FAILURE(status)) {
 | |
|                 if (valueDeleter) {
 | |
|                     valueDeleter(value);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             values->addElementX(fValues, status);
 | |
|             fValues = values;
 | |
|             fHasValuesVector = TRUE;
 | |
|         }
 | |
|         // Add the new value.
 | |
|         ((UVector *)fValues)->addElementX(value, status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // TextTrieMapSearchResultHandler class implementation
 | |
| // ---------------------------------------------------
 | |
| TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // TextTrieMap class implementation
 | |
| // ---------------------------------------------------
 | |
| TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
 | |
| : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0), 
 | |
|   fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
 | |
| }
 | |
| 
 | |
| TextTrieMap::~TextTrieMap() {
 | |
|     int32_t index;
 | |
|     for (index = 0; index < fNodesCount; ++index) {
 | |
|         fNodes[index].deleteValues(fValueDeleter);
 | |
|     }
 | |
|     uprv_free(fNodes);
 | |
|     if (fLazyContents != NULL) {
 | |
|         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
 | |
|             if (fValueDeleter) {
 | |
|                 fValueDeleter(fLazyContents->elementAt(i+1));
 | |
|             }
 | |
|         } 
 | |
|         delete fLazyContents;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int32_t TextTrieMap::isEmpty() const {
 | |
|     // Use a separate field for fIsEmpty because it will remain unchanged once the
 | |
|     //   Trie is built, while fNodes and fLazyContents change with the lazy init
 | |
|     //   of the nodes structure.  Trying to test the changing fields has
 | |
|     //   thread safety complications.
 | |
|     return fIsEmpty;
 | |
| }
 | |
| 
 | |
| 
 | |
| //  We defer actually building the TextTrieMap node structure until the first time a
 | |
| //     search is performed.  put() simply saves the parameters in case we do
 | |
| //     eventually need to build it.
 | |
| //     
 | |
| void
 | |
| TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
 | |
|     const UChar *s = sp.get(key, status);
 | |
|     put(s, value, status);
 | |
| }
 | |
| 
 | |
| // This method is designed for a persistent key, such as string key stored in
 | |
| // resource bundle.
 | |
| void
 | |
| TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
 | |
|     fIsEmpty = FALSE;
 | |
|     if (fLazyContents == NULL) {
 | |
|         fLazyContents = new UVector(status);
 | |
|         if (fLazyContents == NULL) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         }
 | |
|     }
 | |
|     if (U_FAILURE(status)) {
 | |
|         if (fValueDeleter) {
 | |
|             fValueDeleter((void*) key);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     U_ASSERT(fLazyContents != NULL);
 | |
| 
 | |
|     UChar *s = const_cast<UChar *>(key);
 | |
|     fLazyContents->addElementX(s, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         if (fValueDeleter) {
 | |
|             fValueDeleter((void*) key);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     fLazyContents->addElementX(value, status);
 | |
| }
 | |
| 
 | |
| void
 | |
| TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
 | |
|     if (fNodes == NULL) {
 | |
|         fNodesCapacity = 512;
 | |
|         fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
 | |
|         if (fNodes == NULL) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return;
 | |
|         }
 | |
|         fNodes[0].clear();  // Init root node.
 | |
|         fNodesCount = 1;
 | |
|     }
 | |
| 
 | |
|     UnicodeString foldedKey;
 | |
|     const UChar *keyBuffer;
 | |
|     int32_t keyLength;
 | |
|     if (fIgnoreCase) {
 | |
|         // Ok to use fastCopyFrom() because we discard the copy when we return.
 | |
|         foldedKey.fastCopyFrom(key).foldCase();
 | |
|         keyBuffer = foldedKey.getBuffer();
 | |
|         keyLength = foldedKey.length();
 | |
|     } else {
 | |
|         keyBuffer = key.getBuffer();
 | |
|         keyLength = key.length();
 | |
|     }
 | |
| 
 | |
|     CharacterNode *node = fNodes;
 | |
|     int32_t index;
 | |
|     for (index = 0; index < keyLength; ++index) {
 | |
|         node = addChildNode(node, keyBuffer[index], status);
 | |
|     }
 | |
|     node->addValue(value, fValueDeleter, status);
 | |
| }
 | |
| 
 | |
| UBool
 | |
| TextTrieMap::growNodes() {
 | |
|     if (fNodesCapacity == 0xffff) {
 | |
|         return FALSE;  // We use 16-bit node indexes.
 | |
|     }
 | |
|     int32_t newCapacity = fNodesCapacity + 1000;
 | |
|     if (newCapacity > 0xffff) {
 | |
|         newCapacity = 0xffff;
 | |
|     }
 | |
|     CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
 | |
|     if (newNodes == NULL) {
 | |
|         return FALSE;
 | |
|     }
 | |
|     uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
 | |
|     uprv_free(fNodes);
 | |
|     fNodes = newNodes;
 | |
|     fNodesCapacity = newCapacity;
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| CharacterNode*
 | |
| TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     // Linear search of the sorted list of children.
 | |
|     uint16_t prevIndex = 0;
 | |
|     uint16_t nodeIndex = parent->fFirstChild;
 | |
|     while (nodeIndex > 0) {
 | |
|         CharacterNode *current = fNodes + nodeIndex;
 | |
|         UChar childCharacter = current->fCharacter;
 | |
|         if (childCharacter == c) {
 | |
|             return current;
 | |
|         } else if (childCharacter > c) {
 | |
|             break;
 | |
|         }
 | |
|         prevIndex = nodeIndex;
 | |
|         nodeIndex = current->fNextSibling;
 | |
|     }
 | |
| 
 | |
|     // Ensure capacity. Grow fNodes[] if needed.
 | |
|     if (fNodesCount == fNodesCapacity) {
 | |
|         int32_t parentIndex = (int32_t)(parent - fNodes);
 | |
|         if (!growNodes()) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return NULL;
 | |
|         }
 | |
|         parent = fNodes + parentIndex;
 | |
|     }
 | |
| 
 | |
|     // Insert a new child node with c in sorted order.
 | |
|     CharacterNode *node = fNodes + fNodesCount;
 | |
|     node->clear();
 | |
|     node->fCharacter = c;
 | |
|     node->fNextSibling = nodeIndex;
 | |
|     if (prevIndex == 0) {
 | |
|         parent->fFirstChild = (uint16_t)fNodesCount;
 | |
|     } else {
 | |
|         fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
 | |
|     }
 | |
|     ++fNodesCount;
 | |
|     return node;
 | |
| }
 | |
| 
 | |
| CharacterNode*
 | |
| TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
 | |
|     // Linear search of the sorted list of children.
 | |
|     uint16_t nodeIndex = parent->fFirstChild;
 | |
|     while (nodeIndex > 0) {
 | |
|         CharacterNode *current = fNodes + nodeIndex;
 | |
|         UChar childCharacter = current->fCharacter;
 | |
|         if (childCharacter == c) {
 | |
|             return current;
 | |
|         } else if (childCharacter > c) {
 | |
|             break;
 | |
|         }
 | |
|         nodeIndex = current->fNextSibling;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| // buildTrie() - The Trie node structure is needed.  Create it from the data that was
 | |
| //               saved at the time the ZoneStringFormatter was created.  The Trie is only
 | |
| //               needed for parsing operations, which are less common than formatting,
 | |
| //               and the Trie is big, which is why its creation is deferred until first use.
 | |
| void TextTrieMap::buildTrie(UErrorCode &status) {
 | |
|     if (fLazyContents != NULL) {
 | |
|         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
 | |
|             const UChar *key = (UChar *)fLazyContents->elementAt(i);
 | |
|             void  *val = fLazyContents->elementAt(i+1);
 | |
|             UnicodeString keyString(TRUE, key, -1);  // Aliasing UnicodeString constructor.
 | |
|             putImpl(keyString, val, status);
 | |
|         }
 | |
|         delete fLazyContents;
 | |
|         fLazyContents = NULL; 
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| TextTrieMap::search(const UnicodeString &text, int32_t start,
 | |
|                   TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
 | |
|     {
 | |
|         // TODO: if locking the mutex for each check proves to be a performance problem,
 | |
|         //       add a flag of type atomic_int32_t to class TextTrieMap, and use only
 | |
|         //       the ICU atomic safe functions for assigning and testing.
 | |
|         //       Don't test the pointer fLazyContents.
 | |
|         //       Don't do unless it's really required.
 | |
| 
 | |
|         // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
 | |
|         static UMutex TextTrieMutex;
 | |
| 
 | |
|         Mutex lock(&TextTrieMutex);
 | |
|         if (fLazyContents != NULL) {
 | |
|             TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
 | |
|             nonConstThis->buildTrie(status);
 | |
|         }
 | |
|     }
 | |
|     if (fNodes == NULL) {
 | |
|         return;
 | |
|     }
 | |
|     search(fNodes, text, start, start, handler, status);
 | |
| }
 | |
| 
 | |
| void
 | |
| TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
 | |
|                   int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     if (node->hasValues()) {
 | |
|         if (!handler->handleMatch(index - start, node, status)) {
 | |
|             return;
 | |
|         }
 | |
|         if (U_FAILURE(status)) {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if (fIgnoreCase) {
 | |
|         // for folding we need to get a complete code point.
 | |
|         // size of character may grow after fold operation;
 | |
|         // then we need to get result as UTF16 code units.
 | |
|         UChar32 c32 = text.char32At(index);
 | |
|         index += U16_LENGTH(c32);
 | |
|         UnicodeString tmp(c32);
 | |
|         tmp.foldCase();
 | |
|         int32_t tmpidx = 0;
 | |
|         while (tmpidx < tmp.length()) {
 | |
|             UChar c = tmp.charAt(tmpidx++);
 | |
|             node = getChildNode(node, c);
 | |
|             if (node == NULL) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     } else {
 | |
|         // here we just get the next UTF16 code unit
 | |
|         UChar c = text.charAt(index++);
 | |
|         node = getChildNode(node, c);
 | |
|     }
 | |
|     if (node != NULL) {
 | |
|         search(node, text, start, index, handler, status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // ZNStringPool class implementation
 | |
| // ---------------------------------------------------
 | |
| static const int32_t POOL_CHUNK_SIZE = 2000;
 | |
| struct ZNStringPoolChunk: public UMemory {
 | |
|     ZNStringPoolChunk    *fNext;                       // Ptr to next pool chunk
 | |
|     int32_t               fLimit;                       // Index to start of unused area at end of fStrings
 | |
|     UChar                 fStrings[POOL_CHUNK_SIZE];    //  Strings array
 | |
|     ZNStringPoolChunk();
 | |
| };
 | |
| 
 | |
| ZNStringPoolChunk::ZNStringPoolChunk() {
 | |
|     fNext = NULL;
 | |
|     fLimit = 0;
 | |
| }
 | |
| 
 | |
| ZNStringPool::ZNStringPool(UErrorCode &status) {
 | |
|     fChunks = NULL;
 | |
|     fHash   = NULL;
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     fChunks = new ZNStringPoolChunk;
 | |
|     if (fChunks == NULL) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     fHash   = uhash_open(uhash_hashUChars      /* keyHash */, 
 | |
|                          uhash_compareUChars   /* keyComp */, 
 | |
|                          uhash_compareUChars   /* valueComp */, 
 | |
|                          &status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| ZNStringPool::~ZNStringPool() {
 | |
|     if (fHash != NULL) {
 | |
|         uhash_close(fHash);
 | |
|         fHash = NULL;
 | |
|     }
 | |
| 
 | |
|     while (fChunks != NULL) {
 | |
|         ZNStringPoolChunk *nextChunk = fChunks->fNext;
 | |
|         delete fChunks;
 | |
|         fChunks = nextChunk;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const UChar EmptyString = 0;
 | |
| 
 | |
| const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
 | |
|     const UChar *pooledString;
 | |
|     if (U_FAILURE(status)) {
 | |
|         return &EmptyString;
 | |
|     }
 | |
| 
 | |
|     pooledString = static_cast<UChar *>(uhash_get(fHash, s));
 | |
|     if (pooledString != NULL) {
 | |
|         return pooledString;
 | |
|     }
 | |
| 
 | |
|     int32_t length = u_strlen(s);
 | |
|     int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
 | |
|     if (remainingLength <= length) {
 | |
|         U_ASSERT(length < POOL_CHUNK_SIZE);
 | |
|         if (length >= POOL_CHUNK_SIZE) {
 | |
|             status = U_INTERNAL_PROGRAM_ERROR;
 | |
|             return &EmptyString;
 | |
|         }
 | |
|         ZNStringPoolChunk *oldChunk = fChunks;
 | |
|         fChunks = new ZNStringPoolChunk;
 | |
|         if (fChunks == NULL) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return &EmptyString;
 | |
|         }
 | |
|         fChunks->fNext = oldChunk;
 | |
|     }
 | |
|     
 | |
|     UChar *destString = &fChunks->fStrings[fChunks->fLimit];
 | |
|     u_strcpy(destString, s);
 | |
|     fChunks->fLimit += (length + 1);
 | |
|     uhash_put(fHash, destString, destString, &status);
 | |
|     return destString;
 | |
| }        
 | |
| 
 | |
| 
 | |
| //
 | |
| //  ZNStringPool::adopt()    Put a string into the hash, but do not copy the string data
 | |
| //                           into the pool's storage.  Used for strings from resource bundles,
 | |
| //                           which will persist for the life of the zone string formatter, and
 | |
| //                           therefore can be used directly without copying.
 | |
| const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
 | |
|     const UChar *pooledString;
 | |
|     if (U_FAILURE(status)) {
 | |
|         return &EmptyString;
 | |
|     }
 | |
|     if (s != NULL) {
 | |
|         pooledString = static_cast<UChar *>(uhash_get(fHash, s));
 | |
|         if (pooledString == NULL) {
 | |
|             UChar *ncs = const_cast<UChar *>(s);
 | |
|             uhash_put(fHash, ncs, ncs, &status);
 | |
|         }
 | |
|     }
 | |
|     return s;
 | |
| }
 | |
| 
 | |
|     
 | |
| const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
 | |
|     UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
 | |
|     return this->get(nonConstStr.getTerminatedBuffer(), status);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * freeze().   Close the hash table that maps to the pooled strings.
 | |
|  *             After freezing, the pool can not be searched or added to,
 | |
|  *             but all existing references to pooled strings remain valid.
 | |
|  *
 | |
|  *             The main purpose is to recover the storage used for the hash.
 | |
|  */
 | |
| void ZNStringPool::freeze() {
 | |
|     uhash_close(fHash);
 | |
|     fHash = NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * This class stores name data for a meta zone or time zone.
 | |
|  */
 | |
| class ZNames : public UMemory {
 | |
| private:
 | |
|     friend class TimeZoneNamesImpl;
 | |
| 
 | |
|     static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) {
 | |
|         switch(type) {
 | |
|         case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION;
 | |
|         case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC;
 | |
|         case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD;
 | |
|         case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT;
 | |
|         case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC;
 | |
|         case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD;
 | |
|         case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT;
 | |
|         default: return UTZNM_INDEX_UNKNOWN;
 | |
|         }
 | |
|     }
 | |
|     static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) {
 | |
|         switch(index) {
 | |
|         case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION;
 | |
|         case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC;
 | |
|         case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD;
 | |
|         case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT;
 | |
|         case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC;
 | |
|         case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD;
 | |
|         case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT;
 | |
|         default: return UTZNM_UNKNOWN;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const UChar* fNames[UTZNM_INDEX_COUNT];
 | |
|     UBool fDidAddIntoTrie;
 | |
| 
 | |
|     // Whether we own the location string, if computed rather than loaded from a bundle.
 | |
|     // A meta zone names instance never has an exemplar location string.
 | |
|     UBool fOwnsLocationName;
 | |
| 
 | |
|     ZNames(const UChar* names[], const UChar* locationName)
 | |
|             : fDidAddIntoTrie(FALSE) {
 | |
|         uprv_memcpy(fNames, names, sizeof(fNames));
 | |
|         if (locationName != NULL) {
 | |
|             fOwnsLocationName = TRUE;
 | |
|             fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName;
 | |
|         } else {
 | |
|             fOwnsLocationName = FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     ~ZNames() {
 | |
|         if (fOwnsLocationName) {
 | |
|             const UChar* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION];
 | |
|             U_ASSERT(locationName != NULL);
 | |
|             uprv_free((void*) locationName);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     static void* createMetaZoneAndPutInCache(UHashtable* cache, const UChar* names[],
 | |
|             const UnicodeString& mzID, UErrorCode& status) {
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|         U_ASSERT(names != NULL);
 | |
| 
 | |
|         // Use the persistent ID as the resource key, so we can
 | |
|         // avoid duplications.
 | |
|         // TODO: Is there a more efficient way, like intern() in Java?
 | |
|         void* key = (void*) ZoneMeta::findMetaZoneID(mzID);
 | |
|         void* value;
 | |
|         if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
 | |
|             value = (void*) EMPTY;
 | |
|         } else {
 | |
|             value = (void*) (new ZNames(names, NULL));
 | |
|             if (value == NULL) {
 | |
|                 status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|         uhash_put(cache, key, value, &status);
 | |
|         return value;
 | |
|     }
 | |
| 
 | |
|     static void* createTimeZoneAndPutInCache(UHashtable* cache, const UChar* names[],
 | |
|             const UnicodeString& tzID, UErrorCode& status) {
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|         U_ASSERT(names != NULL);
 | |
| 
 | |
|         // If necessary, compute the location name from the time zone name.
 | |
|         UChar* locationName = NULL;
 | |
|         if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == NULL) {
 | |
|             UnicodeString locationNameUniStr;
 | |
|             TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
 | |
| 
 | |
|             // Copy the computed location name to the heap
 | |
|             if (locationNameUniStr.length() > 0) {
 | |
|                 const UChar* buff = locationNameUniStr.getTerminatedBuffer();
 | |
|                 int32_t len = sizeof(UChar) * (locationNameUniStr.length() + 1);
 | |
|                 locationName = (UChar*) uprv_malloc(len);
 | |
|                 if (locationName == NULL) {
 | |
|                     status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     return NULL;
 | |
|                 }
 | |
|                 uprv_memcpy(locationName, buff, len);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Use the persistent ID as the resource key, so we can
 | |
|         // avoid duplications.
 | |
|         // TODO: Is there a more efficient way, like intern() in Java?
 | |
|         void* key = (void*) ZoneMeta::findTimeZoneID(tzID);
 | |
|         void* value = (void*) (new ZNames(names, locationName));
 | |
|         if (value == NULL) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return NULL;
 | |
|         }
 | |
|         uhash_put(cache, key, value, &status);
 | |
|         return value;
 | |
|     }
 | |
| 
 | |
|     const UChar* getName(UTimeZoneNameType type) const {
 | |
|         UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
 | |
|         return index >= 0 ? fNames[index] : NULL;
 | |
|     }
 | |
| 
 | |
|     void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
 | |
|         addNamesIntoTrie(mzID, NULL, trie, status);
 | |
|     }
 | |
|     void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) {
 | |
|         addNamesIntoTrie(NULL, tzID, trie, status);
 | |
|     }
 | |
| 
 | |
|     void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie,
 | |
|             UErrorCode& status) {
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|         if (fDidAddIntoTrie) { return; }
 | |
|         fDidAddIntoTrie = TRUE;
 | |
| 
 | |
|         for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) {
 | |
|             const UChar* name = fNames[i];
 | |
|             if (name != NULL) {
 | |
|                 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
 | |
|                 if (nameinfo == NULL) {
 | |
|                     status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     return;
 | |
|                 }
 | |
|                 nameinfo->mzID = mzID;
 | |
|                 nameinfo->tzID = tzID;
 | |
|                 nameinfo->type = getTZNameType((UTimeZoneNameTypeIndex)i);
 | |
|                 trie.put(name, nameinfo, status); // trie.put() takes ownership of the key
 | |
|                 if (U_FAILURE(status)) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     struct ZNamesLoader;
 | |
| };
 | |
| 
 | |
| struct ZNames::ZNamesLoader : public ResourceSink {
 | |
|     const UChar *names[UTZNM_INDEX_COUNT];
 | |
| 
 | |
|     ZNamesLoader() {
 | |
|         clear();
 | |
|     }
 | |
|     virtual ~ZNamesLoader();
 | |
| 
 | |
|     /** Reset for loading another set of names. */
 | |
|     void clear() {
 | |
|         uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
 | |
|     }
 | |
| 
 | |
|     void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) {
 | |
|         if (U_FAILURE(errorCode)) { return; }
 | |
| 
 | |
|         char key[ZID_KEY_MAX + 1];
 | |
|         mergeTimeZoneKey(mzID, key);
 | |
| 
 | |
|         loadNames(zoneStrings, key, errorCode);
 | |
|     }
 | |
| 
 | |
|     void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) {
 | |
|         // Replace "/" with ":".
 | |
|         UnicodeString uKey(tzID);
 | |
|         for (int32_t i = 0; i < uKey.length(); i++) {
 | |
|             if (uKey.charAt(i) == (UChar)0x2F) {
 | |
|                 uKey.setCharAt(i, (UChar)0x3A);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         char key[ZID_KEY_MAX + 1];
 | |
|         uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
 | |
| 
 | |
|         loadNames(zoneStrings, key, errorCode);
 | |
|     }
 | |
| 
 | |
|     void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
 | |
|         U_ASSERT(zoneStrings != NULL);
 | |
|         U_ASSERT(key != NULL);
 | |
|         U_ASSERT(key[0] != '\0');
 | |
| 
 | |
|         UErrorCode localStatus = U_ZERO_ERROR;
 | |
|         clear();
 | |
|         ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
 | |
| 
 | |
|         // Ignore errors, but propagate possible warnings.
 | |
|         if (U_SUCCESS(localStatus)) {
 | |
|             errorCode = localStatus;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
 | |
|         UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
 | |
|         if (type == UTZNM_INDEX_UNKNOWN) { return; }
 | |
|         if (names[type] == NULL) {
 | |
|             int32_t length;
 | |
|             // 'NO_NAME' indicates internally that this field should remain empty.  It will be
 | |
|             // replaced by 'NULL' in getNames()
 | |
|             names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
 | |
|             UErrorCode &errorCode) override {
 | |
|         ResourceTable namesTable = value.getTable(errorCode);
 | |
|         if (U_FAILURE(errorCode)) { return; }
 | |
|         for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
 | |
|             if (value.isNoInheritanceMarker()) {
 | |
|                 setNameIfEmpty(key, NULL, errorCode);
 | |
|             } else {
 | |
|                 setNameIfEmpty(key, &value, errorCode);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
 | |
|         char c0, c1;
 | |
|         if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
 | |
|             return UTZNM_INDEX_UNKNOWN;
 | |
|         }
 | |
|         if (c0 == 'l') {
 | |
|             return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
 | |
|                     c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
 | |
|                         c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
 | |
|         } else if (c0 == 's') {
 | |
|             return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
 | |
|                     c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
 | |
|                         c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
 | |
|         } else if (c0 == 'e' && c1 == 'c') {
 | |
|             return UTZNM_INDEX_EXEMPLAR_LOCATION;
 | |
|         }
 | |
|         return UTZNM_INDEX_UNKNOWN;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Returns an array of names.  It is the caller's responsibility to copy the data into a
 | |
|     * permanent location, as the returned array is owned by the loader instance and may be
 | |
|     * cleared or leave scope.
 | |
|     *
 | |
|     * This is different than Java, where the array will no longer be modified and null
 | |
|     * may be returned.
 | |
|     */
 | |
|     const UChar** getNames() {
 | |
|         // Remove 'NO_NAME' references in the array and replace with 'NULL'
 | |
|         for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
 | |
|             if (names[i] == NO_NAME) {
 | |
|                 names[i] = NULL;
 | |
|             }
 | |
|         }
 | |
|         return names;
 | |
|     }
 | |
| };
 | |
| 
 | |
| ZNames::ZNamesLoader::~ZNamesLoader() {}
 | |
| 
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // The meta zone ID enumeration class
 | |
| // ---------------------------------------------------
 | |
| class MetaZoneIDsEnumeration : public StringEnumeration {
 | |
| public:
 | |
|     MetaZoneIDsEnumeration();
 | |
|     MetaZoneIDsEnumeration(const UVector& mzIDs);
 | |
|     MetaZoneIDsEnumeration(UVector* mzIDs);
 | |
|     virtual ~MetaZoneIDsEnumeration();
 | |
|     static UClassID U_EXPORT2 getStaticClassID(void);
 | |
|     virtual UClassID getDynamicClassID(void) const override;
 | |
|     virtual const UnicodeString* snext(UErrorCode& status) override;
 | |
|     virtual void reset(UErrorCode& status) override;
 | |
|     virtual int32_t count(UErrorCode& status) const override;
 | |
| private:
 | |
|     int32_t fLen;
 | |
|     int32_t fPos;
 | |
|     const UVector* fMetaZoneIDs;
 | |
|     UVector *fLocalVector;
 | |
| };
 | |
| 
 | |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
 | |
| 
 | |
| MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() 
 | |
| : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
 | |
| }
 | |
| 
 | |
| MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) 
 | |
| : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
 | |
|     fLen = fMetaZoneIDs->size();
 | |
| }
 | |
| 
 | |
| MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
 | |
| : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
 | |
|     if (fMetaZoneIDs) {
 | |
|         fLen = fMetaZoneIDs->size();
 | |
|     }
 | |
| }
 | |
| 
 | |
| const UnicodeString*
 | |
| MetaZoneIDsEnumeration::snext(UErrorCode& status) {
 | |
|     if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
 | |
|         unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
 | |
|         return &unistr;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
 | |
|     fPos = 0;
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
 | |
|     return fLen;
 | |
| }
 | |
| 
 | |
| MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
 | |
|     if (fLocalVector) {
 | |
|         delete fLocalVector;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // ZNameSearchHandler
 | |
| // ---------------------------------------------------
 | |
| class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
 | |
| public:
 | |
|     ZNameSearchHandler(uint32_t types);
 | |
|     virtual ~ZNameSearchHandler();
 | |
| 
 | |
|     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
 | |
|     TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
 | |
| 
 | |
| private:
 | |
|     uint32_t fTypes;
 | |
|     int32_t fMaxMatchLen;
 | |
|     TimeZoneNames::MatchInfoCollection* fResults;
 | |
| };
 | |
| 
 | |
| ZNameSearchHandler::ZNameSearchHandler(uint32_t types) 
 | |
| : fTypes(types), fMaxMatchLen(0), fResults(NULL) {
 | |
| }
 | |
| 
 | |
| ZNameSearchHandler::~ZNameSearchHandler() {
 | |
|     if (fResults != NULL) {
 | |
|         delete fResults;
 | |
|     }
 | |
| }
 | |
| 
 | |
| UBool
 | |
| ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return FALSE;
 | |
|     }
 | |
|     if (node->hasValues()) {
 | |
|         int32_t valuesCount = node->countValues();
 | |
|         for (int32_t i = 0; i < valuesCount; i++) {
 | |
|             ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
 | |
|             if (nameinfo == NULL) {
 | |
|                 continue;
 | |
|             }
 | |
|             if ((nameinfo->type & fTypes) != 0) {
 | |
|                 // matches a requested type
 | |
|                 if (fResults == NULL) {
 | |
|                     fResults = new TimeZoneNames::MatchInfoCollection();
 | |
|                     if (fResults == NULL) {
 | |
|                         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     }
 | |
|                 }
 | |
|                 if (U_SUCCESS(status)) {
 | |
|                     U_ASSERT(fResults != NULL);
 | |
|                     if (nameinfo->tzID) {
 | |
|                         fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
 | |
|                     } else {
 | |
|                         U_ASSERT(nameinfo->mzID);
 | |
|                         fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
 | |
|                     }
 | |
|                     if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
 | |
|                         fMaxMatchLen = matchLength;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| TimeZoneNames::MatchInfoCollection*
 | |
| ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
 | |
|     // give the ownership to the caller
 | |
|     TimeZoneNames::MatchInfoCollection* results = fResults;
 | |
|     maxMatchLen = fMaxMatchLen;
 | |
| 
 | |
|     // reset
 | |
|     fResults = NULL;
 | |
|     fMaxMatchLen = 0;
 | |
|     return results;
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // TimeZoneNamesImpl
 | |
| //
 | |
| // TimeZoneNames implementation class. This is the main
 | |
| // part of this module.
 | |
| // ---------------------------------------------------
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| /**
 | |
|  * Deleter for ZNames
 | |
|  */
 | |
| static void U_CALLCONV
 | |
| deleteZNames(void *obj) {
 | |
|     if (obj != EMPTY) {
 | |
|         delete (ZNames*) obj;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Deleter for ZNameInfo
 | |
|  */
 | |
| static void U_CALLCONV
 | |
| deleteZNameInfo(void *obj) {
 | |
|     uprv_free(obj);
 | |
| }
 | |
| 
 | |
| U_CDECL_END
 | |
| 
 | |
| TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
 | |
| : fLocale(locale),
 | |
|   fZoneStrings(NULL),
 | |
|   fTZNamesMap(NULL),
 | |
|   fMZNamesMap(NULL),
 | |
|   fNamesTrieFullyLoaded(FALSE),
 | |
|   fNamesFullyLoaded(FALSE),
 | |
|   fNamesTrie(TRUE, deleteZNameInfo) {
 | |
|     initialize(locale, status);
 | |
| }
 | |
| 
 | |
| void
 | |
| TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Load zoneStrings bundle
 | |
|     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
 | |
|     fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
 | |
|     fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
 | |
|     if (U_FAILURE(tmpsts)) {
 | |
|         status = tmpsts;
 | |
|         cleanup();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Initialize hashtables holding time zone/meta zone names
 | |
|     fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
 | |
|     fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         cleanup();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     uhash_setValueDeleter(fMZNamesMap, deleteZNames);
 | |
|     uhash_setValueDeleter(fTZNamesMap, deleteZNames);
 | |
|     // no key deleters for name maps
 | |
| 
 | |
|     // preload zone strings for the default zone
 | |
|     TimeZone *tz = TimeZone::createDefault();
 | |
|     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
 | |
|     if (tzID != NULL) {
 | |
|         loadStrings(UnicodeString(tzID), status);
 | |
|     }
 | |
|     delete tz;
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This method updates the cache and must be called with a lock,
 | |
|  * except initializer.
 | |
|  */
 | |
| void
 | |
| TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
 | |
|     loadTimeZoneNames(tzCanonicalID, status);
 | |
|     LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
 | |
|     if (U_FAILURE(status)) { return; }
 | |
|     U_ASSERT(!mzIDs.isNull());
 | |
| 
 | |
|     const UnicodeString *mzID;
 | |
|     while (((mzID = mzIDs->snext(status)) != NULL) && U_SUCCESS(status)) {
 | |
|         loadMetaZoneNames(*mzID, status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TimeZoneNamesImpl::~TimeZoneNamesImpl() {
 | |
|     cleanup();
 | |
| }
 | |
| 
 | |
| void
 | |
| TimeZoneNamesImpl::cleanup() {
 | |
|     if (fZoneStrings != NULL) {
 | |
|         ures_close(fZoneStrings);
 | |
|         fZoneStrings = NULL;
 | |
|     }
 | |
|     if (fMZNamesMap != NULL) {
 | |
|         uhash_close(fMZNamesMap);
 | |
|         fMZNamesMap = NULL;
 | |
|     }
 | |
|     if (fTZNamesMap != NULL) {
 | |
|         uhash_close(fTZNamesMap);
 | |
|         fTZNamesMap = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool
 | |
| TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
 | |
|     if (this == &other) {
 | |
|         return true;
 | |
|     }
 | |
|     // No implementation for now
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| TimeZoneNamesImpl*
 | |
| TimeZoneNamesImpl::clone() const {
 | |
|     UErrorCode status = U_ZERO_ERROR;
 | |
|     return new TimeZoneNamesImpl(fLocale, status);
 | |
| }
 | |
| 
 | |
| StringEnumeration*
 | |
| TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
 | |
|     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
 | |
| }
 | |
| 
 | |
| // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
 | |
| StringEnumeration*
 | |
| TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
 | |
|     if (mzIDs == NULL) {
 | |
|         return new MetaZoneIDsEnumeration();
 | |
|     }
 | |
|     return new MetaZoneIDsEnumeration(*mzIDs);
 | |
| }
 | |
| 
 | |
| StringEnumeration*
 | |
| TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
 | |
|     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
 | |
| }
 | |
| 
 | |
| // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
 | |
| StringEnumeration*
 | |
| TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
 | |
|     if (mappings == NULL) {
 | |
|         return new MetaZoneIDsEnumeration();
 | |
|     }
 | |
| 
 | |
|     MetaZoneIDsEnumeration *senum = NULL;
 | |
|     UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
 | |
|     if (mzIDs == NULL) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|     }
 | |
|     if (U_SUCCESS(status)) {
 | |
|         U_ASSERT(mzIDs != NULL);
 | |
|         for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
 | |
| 
 | |
|             OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
 | |
|             const UChar *mzID = map->mzid;
 | |
|             if (!mzIDs->contains((void *)mzID)) {
 | |
|                 mzIDs->addElementX((void *)mzID, status);
 | |
|             }
 | |
|         }
 | |
|         if (U_SUCCESS(status)) {
 | |
|             senum = new MetaZoneIDsEnumeration(mzIDs);
 | |
|         } else {
 | |
|             delete mzIDs;
 | |
|         }
 | |
|     }
 | |
|     return senum;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
 | |
|     return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
 | |
| }
 | |
| 
 | |
| // static implementation of getMetaZoneID
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
 | |
|     ZoneMeta::getMetazoneID(tzID, date, mzID);
 | |
|     return mzID;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
 | |
|     return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
 | |
| }
 | |
| 
 | |
| // static implementation of getReferenceZoneID
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
 | |
|     ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
 | |
|     return tzID;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
 | |
|                                           UTimeZoneNameType type,
 | |
|                                           UnicodeString& name) const {
 | |
|     name.setToBogus();  // cleanup result.
 | |
|     if (mzID.isEmpty()) {
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     ZNames *znames = NULL;
 | |
|     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 | |
| 
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
|         UErrorCode status = U_ZERO_ERROR;
 | |
|         znames = nonConstThis->loadMetaZoneNames(mzID, status);
 | |
|         if (U_FAILURE(status)) { return name; }
 | |
|     }
 | |
| 
 | |
|     if (znames != NULL) {
 | |
|         const UChar* s = znames->getName(type);
 | |
|         if (s != NULL) {
 | |
|             name.setTo(TRUE, s, -1);
 | |
|         }
 | |
|     }
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
 | |
|     name.setToBogus();  // cleanup result.
 | |
|     if (tzID.isEmpty()) {
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     ZNames *tznames = NULL;
 | |
|     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 | |
| 
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
|         UErrorCode status = U_ZERO_ERROR;
 | |
|         tznames = nonConstThis->loadTimeZoneNames(tzID, status);
 | |
|         if (U_FAILURE(status)) { return name; }
 | |
|     }
 | |
| 
 | |
|     if (tznames != NULL) {
 | |
|         const UChar *s = tznames->getName(type);
 | |
|         if (s != NULL) {
 | |
|             name.setTo(TRUE, s, -1);
 | |
|         }
 | |
|     }
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
 | |
|     name.setToBogus();  // cleanup result.
 | |
|     const UChar* locName = NULL;
 | |
|     ZNames *tznames = NULL;
 | |
|     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 | |
| 
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
|         UErrorCode status = U_ZERO_ERROR;
 | |
|         tznames = nonConstThis->loadTimeZoneNames(tzID, status);
 | |
|         if (U_FAILURE(status)) { return name; }
 | |
|     }
 | |
| 
 | |
|     if (tznames != NULL) {
 | |
|         locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
 | |
|     }
 | |
|     if (locName != NULL) {
 | |
|         name.setTo(TRUE, locName, -1);
 | |
|     }
 | |
| 
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Merge the MZ_PREFIX and mzId
 | |
| static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
 | |
|     if (mzID.isEmpty()) {
 | |
|         result[0] = '\0';
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     char mzIdChar[ZID_KEY_MAX + 1];
 | |
|     int32_t keyLen;
 | |
|     int32_t prefixLen = static_cast<int32_t>(uprv_strlen(gMZPrefix));
 | |
|     keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
 | |
|     uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
 | |
|     uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
 | |
|     result[keyLen + prefixLen] = '\0';
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This method updates the cache and must be called with a lock
 | |
|  */
 | |
| ZNames*
 | |
| TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) { return NULL; }
 | |
|     U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
 | |
| 
 | |
|     UChar mzIDKey[ZID_KEY_MAX + 1];
 | |
|     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
 | |
|     U_ASSERT(U_SUCCESS(status));   // already checked length above
 | |
|     mzIDKey[mzID.length()] = 0;
 | |
| 
 | |
|     void* mznames = uhash_get(fMZNamesMap, mzIDKey);
 | |
|     if (mznames == NULL) {
 | |
|         ZNames::ZNamesLoader loader;
 | |
|         loader.loadMetaZone(fZoneStrings, mzID, status);
 | |
|         mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|     }
 | |
| 
 | |
|     if (mznames != EMPTY) {
 | |
|         return (ZNames*)mznames;
 | |
|     } else {
 | |
|         return NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This method updates the cache and must be called with a lock
 | |
|  */
 | |
| ZNames*
 | |
| TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) { return NULL; }
 | |
|     U_ASSERT(tzID.length() <= ZID_KEY_MAX);
 | |
| 
 | |
|     UChar tzIDKey[ZID_KEY_MAX + 1];
 | |
|     int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
 | |
|     U_ASSERT(U_SUCCESS(status));   // already checked length above
 | |
|     tzIDKey[tzIDKeyLen] = 0;
 | |
| 
 | |
|     void *tznames = uhash_get(fTZNamesMap, tzIDKey);
 | |
|     if (tznames == NULL) {
 | |
|         ZNames::ZNamesLoader loader;
 | |
|         loader.loadTimeZone(fZoneStrings, tzID, status);
 | |
|         tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|     }
 | |
| 
 | |
|     // tznames is never EMPTY
 | |
|     return (ZNames*)tznames;
 | |
| }
 | |
| 
 | |
| TimeZoneNames::MatchInfoCollection*
 | |
| TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
 | |
|     ZNameSearchHandler handler(types);
 | |
|     TimeZoneNames::MatchInfoCollection* matches;
 | |
|     TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
 | |
| 
 | |
|     // Synchronize so that data is not loaded multiple times.
 | |
|     // TODO: Consider more fine-grained synchronization.
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
| 
 | |
|         // First try of lookup.
 | |
|         matches = doFind(handler, text, start, status);
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|         if (matches != NULL) {
 | |
|             return matches;
 | |
|         }
 | |
| 
 | |
|         // All names are not yet loaded into the trie.
 | |
|         // We may have loaded names for formatting several time zones,
 | |
|         // and might be parsing one of those.
 | |
|         // Populate the parsing trie from all of the already-loaded names.
 | |
|         nonConstThis->addAllNamesIntoTrie(status);
 | |
| 
 | |
|         // Second try of lookup.
 | |
|         matches = doFind(handler, text, start, status);
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
|         if (matches != NULL) {
 | |
|             return matches;
 | |
|         }
 | |
| 
 | |
|         // There are still some names we haven't loaded into the trie yet.
 | |
|         // Load everything now.
 | |
|         nonConstThis->internalLoadAllDisplayNames(status);
 | |
|         nonConstThis->addAllNamesIntoTrie(status);
 | |
|         nonConstThis->fNamesTrieFullyLoaded = TRUE;
 | |
|         if (U_FAILURE(status)) { return NULL; }
 | |
| 
 | |
|         // Third try: we must return this one.
 | |
|         return doFind(handler, text, start, status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TimeZoneNames::MatchInfoCollection*
 | |
| TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
 | |
|         const UnicodeString& text, int32_t start, UErrorCode& status) const {
 | |
| 
 | |
|     fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
 | |
|     if (U_FAILURE(status)) { return NULL; }
 | |
| 
 | |
|     int32_t maxLen = 0;
 | |
|     TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
 | |
|     if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
 | |
|         // perfect match, or no more names available
 | |
|         return matches;
 | |
|     }
 | |
|     delete matches;
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| // Caller must synchronize.
 | |
| void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) return;
 | |
|     int32_t pos;
 | |
|     const UHashElement* element;
 | |
| 
 | |
|     pos = UHASH_FIRST;
 | |
|     while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) {
 | |
|         if (element->value.pointer == EMPTY) { continue; }
 | |
|         UChar* mzID = (UChar*) element->key.pointer;
 | |
|         ZNames* znames = (ZNames*) element->value.pointer;
 | |
|         znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|     }
 | |
| 
 | |
|     pos = UHASH_FIRST;
 | |
|     while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) {
 | |
|         if (element->value.pointer == EMPTY) { continue; }
 | |
|         UChar* tzID = (UChar*) element->key.pointer;
 | |
|         ZNames* znames = (ZNames*) element->value.pointer;
 | |
|         znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|     }
 | |
| }
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| static void U_CALLCONV
 | |
| deleteZNamesLoader(void* obj) {
 | |
|     if (obj == DUMMY_LOADER) { return; }
 | |
|     const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
 | |
|     delete loader;
 | |
| }
 | |
| U_CDECL_END
 | |
| 
 | |
| struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
 | |
|     TimeZoneNamesImpl& tzn;
 | |
|     UHashtable* keyToLoader;
 | |
| 
 | |
|     ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
 | |
|             : tzn(_tzn) {
 | |
|         keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|         uhash_setKeyDeleter(keyToLoader, uprv_free);
 | |
|         uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
 | |
|     }
 | |
|     virtual ~ZoneStringsLoader();
 | |
| 
 | |
|     void* createKey(const char* key, UErrorCode& status) {
 | |
|         int32_t len = sizeof(char) * (static_cast<int32_t>(uprv_strlen(key)) + 1);
 | |
|         char* newKey = (char*) uprv_malloc(len);
 | |
|         if (newKey == NULL) {
 | |
|             status = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return NULL;
 | |
|         }
 | |
|         uprv_memcpy(newKey, key, len);
 | |
|         newKey[len-1] = '\0';
 | |
|         return (void*) newKey;
 | |
|     }
 | |
| 
 | |
|     UBool isMetaZone(const char* key) {
 | |
|         return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
 | |
|     }
 | |
| 
 | |
|     UnicodeString mzIDFromKey(const char* key) {
 | |
|         return UnicodeString(key + MZ_PREFIX_LEN, static_cast<int32_t>(uprv_strlen(key)) - MZ_PREFIX_LEN, US_INV);
 | |
|     }
 | |
| 
 | |
|     UnicodeString tzIDFromKey(const char* key) {
 | |
|         UnicodeString tzID(key, -1, US_INV);
 | |
|         // Replace all colons ':' with slashes '/'
 | |
|         for (int i=0; i<tzID.length(); i++) {
 | |
|             if (tzID.charAt(i) == 0x003A) {
 | |
|                 tzID.setCharAt(i, 0x002F);
 | |
|             }
 | |
|         }
 | |
|         return tzID;
 | |
|     }
 | |
| 
 | |
|     void load(UErrorCode& status) {
 | |
|         ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
| 
 | |
|         int32_t pos = UHASH_FIRST;
 | |
|         const UHashElement* element;
 | |
|         while ((element = uhash_nextElement(keyToLoader, &pos)) != NULL) {
 | |
|             if (element->value.pointer == DUMMY_LOADER) { continue; }
 | |
|             ZNames::ZNamesLoader* loader = (ZNames::ZNamesLoader*) element->value.pointer;
 | |
|             char* key = (char*) element->key.pointer;
 | |
| 
 | |
|             if (isMetaZone(key)) {
 | |
|                 UnicodeString mzID = mzIDFromKey(key);
 | |
|                 ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
 | |
|             } else {
 | |
|                 UnicodeString tzID = tzIDFromKey(key);
 | |
|                 ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
 | |
|             }
 | |
|             if (U_FAILURE(status)) { return; }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
 | |
|             UErrorCode &status) {
 | |
|         if (U_FAILURE(status)) { return; }
 | |
| 
 | |
|         void* loader = uhash_get(keyToLoader, key);
 | |
|         if (loader == NULL) {
 | |
|             if (isMetaZone(key)) {
 | |
|                 UnicodeString mzID = mzIDFromKey(key);
 | |
|                 void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
 | |
|                 if (cacheVal != NULL) {
 | |
|                     // We have already loaded the names for this meta zone.
 | |
|                     loader = (void*) DUMMY_LOADER;
 | |
|                 } else {
 | |
|                     loader = (void*) new ZNames::ZNamesLoader();
 | |
|                     if (loader == NULL) {
 | |
|                         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                         return;
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
|                 UnicodeString tzID = tzIDFromKey(key);
 | |
|                 void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
 | |
|                 if (cacheVal != NULL) {
 | |
|                     // We have already loaded the names for this time zone.
 | |
|                     loader = (void*) DUMMY_LOADER;
 | |
|                 } else {
 | |
|                     loader = (void*) new ZNames::ZNamesLoader();
 | |
|                     if (loader == NULL) {
 | |
|                         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                         return;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void* newKey = createKey(key, status);
 | |
|             if (U_FAILURE(status)) {
 | |
|                 deleteZNamesLoader(loader);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             uhash_put(keyToLoader, newKey, loader, &status);
 | |
|             if (U_FAILURE(status)) { return; }
 | |
|         }
 | |
| 
 | |
|         if (loader != DUMMY_LOADER) {
 | |
|             // Let the ZNamesLoader consume the names table.
 | |
|             ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     virtual void put(const char *key, ResourceValue &value, UBool noFallback,
 | |
|             UErrorCode &status) override {
 | |
|         ResourceTable timeZonesTable = value.getTable(status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|         for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
 | |
|             U_ASSERT(!value.isNoInheritanceMarker());
 | |
|             if (value.getType() == URES_TABLE) {
 | |
|                 consumeNamesTable(key, value, noFallback, status);
 | |
|             } else {
 | |
|                 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
 | |
|                 // All time zone fields are tables.
 | |
|             }
 | |
|             if (U_FAILURE(status)) { return; }
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| // Virtual destructors must be defined out of line.
 | |
| TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
 | |
|     uhash_close(keyToLoader);
 | |
| }
 | |
| 
 | |
| void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) return;
 | |
| 
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
|         internalLoadAllDisplayNames(status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
 | |
|         const UTimeZoneNameType types[], int32_t numTypes,
 | |
|         UDate date, UnicodeString dest[], UErrorCode& status) const {
 | |
|     if (U_FAILURE(status)) return;
 | |
| 
 | |
|     if (tzID.isEmpty()) { return; }
 | |
|     void* tznames = NULL;
 | |
|     void* mznames = NULL;
 | |
|     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
 | |
| 
 | |
|     // Load the time zone strings
 | |
|     {
 | |
|         Mutex lock(&gDataMutex);
 | |
|         tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
|     }
 | |
|     U_ASSERT(tznames != NULL);
 | |
| 
 | |
|     // Load the values into the dest array
 | |
|     for (int i = 0; i < numTypes; i++) {
 | |
|         UTimeZoneNameType type = types[i];
 | |
|         const UChar* name = ((ZNames*)tznames)->getName(type);
 | |
|         if (name == NULL) {
 | |
|             if (mznames == NULL) {
 | |
|                 // Load the meta zone name
 | |
|                 UnicodeString mzID;
 | |
|                 getMetaZoneID(tzID, date, mzID);
 | |
|                 if (mzID.isEmpty()) {
 | |
|                     mznames = (void*) EMPTY;
 | |
|                 } else {
 | |
|                     // Load the meta zone strings
 | |
|                     // Mutex is scoped to the "else" statement
 | |
|                     Mutex lock(&gDataMutex);
 | |
|                     mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
 | |
|                     if (U_FAILURE(status)) { return; }
 | |
|                     // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
 | |
|                     // a dummy object instead of NULL.
 | |
|                     if (mznames == NULL) {
 | |
|                         mznames = (void*) EMPTY;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             U_ASSERT(mznames != NULL);
 | |
|             if (mznames != EMPTY) {
 | |
|                 name = ((ZNames*)mznames)->getName(type);
 | |
|             }
 | |
|         }
 | |
|         if (name != NULL) {
 | |
|             dest[i].setTo(TRUE, name, -1);
 | |
|         } else {
 | |
|             dest[i].setToBogus();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Caller must synchronize.
 | |
| void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
 | |
|     if (!fNamesFullyLoaded) {
 | |
|         fNamesFullyLoaded = TRUE;
 | |
| 
 | |
|         ZoneStringsLoader loader(*this, status);
 | |
|         loader.load(status);
 | |
|         if (U_FAILURE(status)) { return; }
 | |
| 
 | |
|         const UnicodeString *id;
 | |
| 
 | |
|         // load strings for all zones
 | |
|         StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
 | |
|             UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
 | |
|         if (U_SUCCESS(status)) {
 | |
|             while ((id = tzIDs->snext(status)) != NULL) {
 | |
|                 if (U_FAILURE(status)) {
 | |
|                     break;
 | |
|                 }
 | |
|                 UnicodeString copy(*id);
 | |
|                 void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
 | |
|                 if (value == NULL) {
 | |
|                     // loadStrings also loads related metazone strings
 | |
|                     loadStrings(*id, status);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (tzIDs != NULL) {
 | |
|             delete tzIDs;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static const UChar gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
 | |
| static const int32_t gEtcPrefixLen      = 4;
 | |
| static const UChar gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
 | |
| static const int32_t gSystemVPrefixLen  = 8;
 | |
| static const UChar gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
 | |
| static const int32_t gRiyadh8Len       = 7;
 | |
| 
 | |
| UnicodeString& U_EXPORT2
 | |
| TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
 | |
|     if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
 | |
|         || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
 | |
|         name.setToBogus();
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
 | |
|     if (sep > 0 && sep + 1 < tzID.length()) {
 | |
|         name.setTo(tzID, sep + 1);
 | |
|         name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
 | |
|                             UnicodeString((UChar)0x20 /* space */));
 | |
|     } else {
 | |
|         name.setToBogus();
 | |
|     }
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------
 | |
| // TZDBTimeZoneNames and its supporting classes
 | |
| //
 | |
| // TZDBTimeZoneNames is an implementation class of
 | |
| // TimeZoneNames holding the IANA tz database abbreviations.
 | |
| // ---------------------------------------------------
 | |
| 
 | |
| class TZDBNames : public UMemory {
 | |
| public:
 | |
|     virtual ~TZDBNames();
 | |
| 
 | |
|     static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
 | |
|     const UChar* getName(UTimeZoneNameType type) const;
 | |
|     const char** getParseRegions(int32_t& numRegions) const;
 | |
| 
 | |
| protected:
 | |
|     TZDBNames(const UChar** names, char** regions, int32_t numRegions);
 | |
| 
 | |
| private:
 | |
|     const UChar** fNames;
 | |
|     char** fRegions;
 | |
|     int32_t fNumRegions;
 | |
| };
 | |
| 
 | |
| TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions)
 | |
|     :   fNames(names),
 | |
|         fRegions(regions),
 | |
|         fNumRegions(numRegions) {
 | |
| }
 | |
| 
 | |
| TZDBNames::~TZDBNames() {
 | |
|     if (fNames != NULL) {
 | |
|         uprv_free(fNames);
 | |
|     }
 | |
|     if (fRegions != NULL) {
 | |
|         char **p = fRegions;
 | |
|         for (int32_t i = 0; i < fNumRegions; p++, i++) {
 | |
|             uprv_free(*p);
 | |
|         }
 | |
|         uprv_free(fRegions);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TZDBNames*
 | |
| TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
 | |
|     if (rb == NULL || key == NULL || *key == 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     UErrorCode status = U_ZERO_ERROR;
 | |
| 
 | |
|     const UChar **names = NULL;
 | |
|     char** regions = NULL;
 | |
|     int32_t numRegions = 0;
 | |
| 
 | |
|     int32_t len = 0;
 | |
| 
 | |
|     UResourceBundle* rbTable = NULL;
 | |
|     rbTable = ures_getByKey(rb, key, rbTable, &status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE);
 | |
|     UBool isEmpty = TRUE;
 | |
|     if (names != NULL) {
 | |
|         for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
 | |
|             status = U_ZERO_ERROR;
 | |
|             const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
 | |
|             if (U_FAILURE(status) || len == 0) {
 | |
|                 names[i] = NULL;
 | |
|             } else {
 | |
|                 names[i] = value;
 | |
|                 isEmpty = FALSE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (isEmpty) {
 | |
|         if (names != NULL) {
 | |
|             uprv_free(names);
 | |
|         }
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status);
 | |
|     UBool regionError = FALSE;
 | |
|     if (U_SUCCESS(status)) {
 | |
|         numRegions = ures_getSize(regionsRes);
 | |
|         if (numRegions > 0) {
 | |
|             regions = (char**)uprv_malloc(sizeof(char*) * numRegions);
 | |
|             if (regions != NULL) {
 | |
|                 char **pRegion = regions;
 | |
|                 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
 | |
|                     *pRegion = NULL;
 | |
|                 }
 | |
|                 // filling regions
 | |
|                 pRegion = regions;
 | |
|                 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
 | |
|                     status = U_ZERO_ERROR;
 | |
|                     const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
 | |
|                     if (U_FAILURE(status)) {
 | |
|                         regionError = TRUE;
 | |
|                         break;
 | |
|                     }
 | |
|                     *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1));
 | |
|                     if (*pRegion == NULL) {
 | |
|                         regionError = TRUE;
 | |
|                         break;
 | |
|                     }
 | |
|                     u_UCharsToChars(uregion, *pRegion, len);
 | |
|                     (*pRegion)[len] = 0;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     ures_close(regionsRes);
 | |
|     ures_close(rbTable);
 | |
| 
 | |
|     if (regionError) {
 | |
|         if (names != NULL) {
 | |
|             uprv_free(names);
 | |
|         }
 | |
|         if (regions != NULL) {
 | |
|             char **p = regions;
 | |
|             for (int32_t i = 0; i < numRegions; p++, i++) {
 | |
|                 uprv_free(*p);
 | |
|             }
 | |
|             uprv_free(regions);
 | |
|         }
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return new TZDBNames(names, regions, numRegions);
 | |
| }
 | |
| 
 | |
| const UChar*
 | |
| TZDBNames::getName(UTimeZoneNameType type) const {
 | |
|     if (fNames == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     const UChar *name = NULL;
 | |
|     switch(type) {
 | |
|     case UTZNM_SHORT_STANDARD:
 | |
|         name = fNames[0];
 | |
|         break;
 | |
|     case UTZNM_SHORT_DAYLIGHT:
 | |
|         name = fNames[1];
 | |
|         break;
 | |
|     default:
 | |
|         name = NULL;
 | |
|     }
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| const char**
 | |
| TZDBNames::getParseRegions(int32_t& numRegions) const {
 | |
|     if (fRegions == NULL) {
 | |
|         numRegions = 0;
 | |
|     } else {
 | |
|         numRegions = fNumRegions;
 | |
|     }
 | |
|     return (const char**)fRegions;
 | |
| }
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| /**
 | |
|  * TZDBNameInfo stores metazone name information for the IANA abbreviations
 | |
|  * in the trie
 | |
|  */
 | |
| typedef struct TZDBNameInfo {
 | |
|     const UChar*        mzID;
 | |
|     UTimeZoneNameType   type;
 | |
|     UBool               ambiguousType;
 | |
|     const char**        parseRegions;
 | |
|     int32_t             nRegions;
 | |
| } TZDBNameInfo;
 | |
| U_CDECL_END
 | |
| 
 | |
| 
 | |
| class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
 | |
| public:
 | |
|     TZDBNameSearchHandler(uint32_t types, const char* region);
 | |
|     virtual ~TZDBNameSearchHandler();
 | |
| 
 | |
|     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
 | |
|     TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
 | |
| 
 | |
| private:
 | |
|     uint32_t fTypes;
 | |
|     int32_t fMaxMatchLen;
 | |
|     TimeZoneNames::MatchInfoCollection* fResults;
 | |
|     const char* fRegion;
 | |
| };
 | |
| 
 | |
| TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region) 
 | |
| : fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) {
 | |
| }
 | |
| 
 | |
| TZDBNameSearchHandler::~TZDBNameSearchHandler() {
 | |
|     if (fResults != NULL) {
 | |
|         delete fResults;
 | |
|     }
 | |
| }
 | |
| 
 | |
| UBool
 | |
| TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     TZDBNameInfo *match = NULL;
 | |
|     TZDBNameInfo *defaultRegionMatch = NULL;
 | |
| 
 | |
|     if (node->hasValues()) {
 | |
|         int32_t valuesCount = node->countValues();
 | |
|         for (int32_t i = 0; i < valuesCount; i++) {
 | |
|             TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
 | |
|             if (ninfo == NULL) {
 | |
|                 continue;
 | |
|             }
 | |
|             if ((ninfo->type & fTypes) != 0) {
 | |
|                 // Some tz database abbreviations are ambiguous. For example,
 | |
|                 // CST means either Central Standard Time or China Standard Time.
 | |
|                 // Unlike CLDR time zone display names, this implementation
 | |
|                 // does not use unique names. And TimeZoneFormat does not expect
 | |
|                 // multiple results returned for the same time zone type.
 | |
|                 // For this reason, this implementation resolve one among same
 | |
|                 // zone type with a same name at this level.
 | |
|                 if (ninfo->parseRegions == NULL) {
 | |
|                     // parseRegions == null means this is the default metazone
 | |
|                     // mapping for the abbreviation.
 | |
|                     if (defaultRegionMatch == NULL) {
 | |
|                         match = defaultRegionMatch = ninfo;
 | |
|                     }
 | |
|                 } else {
 | |
|                     UBool matchRegion = FALSE;
 | |
|                     // non-default metazone mapping for an abbreviation
 | |
|                     // comes with applicable regions. For example, the default
 | |
|                     // metazone mapping for "CST" is America_Central,
 | |
|                     // but if region is one of CN/MO/TW, "CST" is parsed
 | |
|                     // as metazone China (China Standard Time).
 | |
|                     for (int32_t j = 0; j < ninfo->nRegions; j++) {
 | |
|                         const char *region = ninfo->parseRegions[j];
 | |
|                         if (uprv_strcmp(fRegion, region) == 0) {
 | |
|                             match = ninfo;
 | |
|                             matchRegion = TRUE;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                     if (matchRegion) {
 | |
|                         break;
 | |
|                     }
 | |
|                     if (match == NULL) {
 | |
|                         match = ninfo;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (match != NULL) {
 | |
|             UTimeZoneNameType ntype = match->type;
 | |
|             // Note: Workaround for duplicated standard/daylight names
 | |
|             // The tz database contains a few zones sharing a
 | |
|             // same name for both standard time and daylight saving
 | |
|             // time. For example, Australia/Sydney observes DST,
 | |
|             // but "EST" is used for both standard and daylight.
 | |
|             // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
 | |
|             // in the find operation, we cannot tell which one was
 | |
|             // actually matched.
 | |
|             // TimeZoneFormat#parse returns a matched name type (standard
 | |
|             // or daylight) and DateFormat implementation uses the info to
 | |
|             // to adjust actual time. To avoid false type information,
 | |
|             // this implementation replaces the name type with SHORT_GENERIC.
 | |
|             if (match->ambiguousType
 | |
|                     && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
 | |
|                     && (fTypes & UTZNM_SHORT_STANDARD) != 0
 | |
|                     && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
 | |
|                 ntype = UTZNM_SHORT_GENERIC;
 | |
|             }
 | |
| 
 | |
|             if (fResults == NULL) {
 | |
|                 fResults = new TimeZoneNames::MatchInfoCollection();
 | |
|                 if (fResults == NULL) {
 | |
|                     status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                 }
 | |
|             }
 | |
|             if (U_SUCCESS(status)) {
 | |
|                 U_ASSERT(fResults != NULL);
 | |
|                 U_ASSERT(match->mzID != NULL);
 | |
|                 fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
 | |
|                 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
 | |
|                     fMaxMatchLen = matchLength;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| TimeZoneNames::MatchInfoCollection*
 | |
| TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
 | |
|     // give the ownership to the caller
 | |
|     TimeZoneNames::MatchInfoCollection* results = fResults;
 | |
|     maxMatchLen = fMaxMatchLen;
 | |
| 
 | |
|     // reset
 | |
|     fResults = NULL;
 | |
|     fMaxMatchLen = 0;
 | |
|     return results;
 | |
| }
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| /**
 | |
|  * Deleter for TZDBNames
 | |
|  */
 | |
| static void U_CALLCONV
 | |
| deleteTZDBNames(void *obj) {
 | |
|     if (obj != EMPTY) {
 | |
|         delete (TZDBNames *)obj;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
 | |
|     gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         gTZDBNamesMap = NULL;
 | |
|         return;
 | |
|     }
 | |
|     // no key deleters for tzdb name maps
 | |
|     uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
 | |
|     ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Deleter for TZDBNameInfo
 | |
|  */
 | |
| static void U_CALLCONV
 | |
| deleteTZDBNameInfo(void *obj) {
 | |
|     if (obj != NULL) {
 | |
|         uprv_free(obj);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void U_CALLCONV prepareFind(UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     gTZDBNamesTrie = new TextTrieMap(TRUE, deleteTZDBNameInfo);
 | |
|     if (gTZDBNamesTrie == NULL) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const UnicodeString *mzID;
 | |
|     StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
 | |
|     if (U_SUCCESS(status)) {
 | |
|         while ((mzID = mzIDs->snext(status)) != 0 && U_SUCCESS(status)) {
 | |
|             const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
 | |
|             if (U_FAILURE(status)) {
 | |
|                 break;
 | |
|             }
 | |
|             if (names == NULL) {
 | |
|                 continue;
 | |
|             }
 | |
|             const UChar *std = names->getName(UTZNM_SHORT_STANDARD);
 | |
|             const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
 | |
|             if (std == NULL && dst == NULL) {
 | |
|                 continue;
 | |
|             }
 | |
|             int32_t numRegions = 0;
 | |
|             const char **parseRegions = names->getParseRegions(numRegions);
 | |
| 
 | |
|             // The tz database contains a few zones sharing a
 | |
|             // same name for both standard time and daylight saving
 | |
|             // time. For example, Australia/Sydney observes DST,
 | |
|             // but "EST" is used for both standard and daylight.
 | |
|             // we need to store the information for later processing.
 | |
|             UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0);
 | |
| 
 | |
|             const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID);
 | |
|             if (std != NULL) {
 | |
|                 TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
 | |
|                 if (stdInf == NULL) {
 | |
|                     status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     break;
 | |
|                 }
 | |
|                 stdInf->mzID = uMzID;
 | |
|                 stdInf->type = UTZNM_SHORT_STANDARD;
 | |
|                 stdInf->ambiguousType = ambiguousType;
 | |
|                 stdInf->parseRegions = parseRegions;
 | |
|                 stdInf->nRegions = numRegions;
 | |
|                 gTZDBNamesTrie->put(std, stdInf, status);
 | |
|             }
 | |
|             if (U_SUCCESS(status) && dst != NULL) {
 | |
|                 TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
 | |
|                 if (dstInf == NULL) {
 | |
|                     status = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     break;
 | |
|                 }
 | |
|                 dstInf->mzID = uMzID;
 | |
|                 dstInf->type = UTZNM_SHORT_DAYLIGHT;
 | |
|                 dstInf->ambiguousType = ambiguousType;
 | |
|                 dstInf->parseRegions = parseRegions;
 | |
|                 dstInf->nRegions = numRegions;
 | |
|                 gTZDBNamesTrie->put(dst, dstInf, status);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     delete mzIDs;
 | |
| 
 | |
|     if (U_FAILURE(status)) {
 | |
|         delete gTZDBNamesTrie;
 | |
|         gTZDBNamesTrie = NULL;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
 | |
| }
 | |
| 
 | |
| U_CDECL_END
 | |
| 
 | |
| TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
 | |
| : fLocale(locale) {
 | |
|     UBool useWorld = TRUE;
 | |
|     const char* region = fLocale.getCountry();
 | |
|     int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
 | |
|     if (regionLen == 0) {
 | |
|         UErrorCode status = U_ZERO_ERROR;
 | |
|         CharString loc;
 | |
|         {
 | |
|             CharStringByteSink sink(&loc);
 | |
|             ulocimp_addLikelySubtags(fLocale.getName(), sink, &status);
 | |
|         }
 | |
|         regionLen = uloc_getCountry(loc.data(), fRegion, sizeof(fRegion), &status);
 | |
|         if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) {
 | |
|             useWorld = FALSE;
 | |
|         }
 | |
|     } else if (regionLen < (int32_t)sizeof(fRegion)) {
 | |
|         uprv_strcpy(fRegion, region);
 | |
|         useWorld = FALSE;
 | |
|     }
 | |
|     if (useWorld) {
 | |
|         uprv_strcpy(fRegion, "001");
 | |
|     }
 | |
| }
 | |
| 
 | |
| TZDBTimeZoneNames::~TZDBTimeZoneNames() {
 | |
| }
 | |
| 
 | |
| bool
 | |
| TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
 | |
|     if (this == &other) {
 | |
|         return true;
 | |
|     }
 | |
|     // No implementation for now
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| TZDBTimeZoneNames*
 | |
| TZDBTimeZoneNames::clone() const {
 | |
|     return new TZDBTimeZoneNames(fLocale);
 | |
| }
 | |
| 
 | |
| StringEnumeration*
 | |
| TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
 | |
|     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
 | |
| }
 | |
| 
 | |
| StringEnumeration*
 | |
| TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
 | |
|     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
 | |
|     return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
 | |
|     return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
 | |
|                                           UTimeZoneNameType type,
 | |
|                                           UnicodeString& name) const {
 | |
|     name.setToBogus();
 | |
|     if (mzID.isEmpty()) {
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     UErrorCode status = U_ZERO_ERROR;
 | |
|     const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
 | |
|     if (U_SUCCESS(status)) {
 | |
|         if (tzdbNames != NULL) {
 | |
|             const UChar *s = tzdbNames->getName(type);
 | |
|             if (s != NULL) {
 | |
|                 name.setTo(TRUE, s, -1);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
 | |
|     // No abbreviations associated a zone directly for now.
 | |
|     name.setToBogus();
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| TZDBTimeZoneNames::MatchInfoCollection*
 | |
| TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
 | |
|     umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     TZDBNameSearchHandler handler(types, fRegion);
 | |
|     gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     int32_t maxLen = 0;
 | |
|     return handler.getMatches(maxLen);
 | |
| }
 | |
| 
 | |
| const TZDBNames*
 | |
| TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
 | |
|     umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     TZDBNames* tzdbNames = NULL;
 | |
| 
 | |
|     UChar mzIDKey[ZID_KEY_MAX + 1];
 | |
|     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
 | |
|     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
 | |
|     mzIDKey[mzID.length()] = 0;
 | |
| 
 | |
|     static UMutex gTZDBNamesMapLock;
 | |
|     umtx_lock(&gTZDBNamesMapLock);
 | |
|     {
 | |
|         void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
 | |
|         if (cacheVal == NULL) {
 | |
|             UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
 | |
|             zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
 | |
|             if (U_SUCCESS(status)) {
 | |
|                 char key[ZID_KEY_MAX + 1];
 | |
|                 mergeTimeZoneKey(mzID, key);
 | |
|                 tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
 | |
| 
 | |
|                 if (tzdbNames == NULL) {
 | |
|                     cacheVal = (void *)EMPTY;
 | |
|                 } else {
 | |
|                     cacheVal = tzdbNames;
 | |
|                 }
 | |
|                 // Use the persistent ID as the resource key, so we can
 | |
|                 // avoid duplications.
 | |
|                 // TODO: Is there a more efficient way, like intern() in Java?
 | |
|                 void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
 | |
|                 if (newKey != NULL) {
 | |
|                     uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
 | |
|                     if (U_FAILURE(status)) {
 | |
|                         if (tzdbNames != NULL) {
 | |
|                             delete tzdbNames;
 | |
|                             tzdbNames = NULL;
 | |
|                         }
 | |
|                     }
 | |
|                 } else {
 | |
|                     // Should never happen with a valid input
 | |
|                     if (tzdbNames != NULL) {
 | |
|                         // It's not possible that we get a valid tzdbNames with unknown ID.
 | |
|                         // But just in case..
 | |
|                         delete tzdbNames;
 | |
|                         tzdbNames = NULL;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             ures_close(zoneStringsRes);
 | |
|         } else if (cacheVal != EMPTY) {
 | |
|             tzdbNames = (TZDBNames *)cacheVal;
 | |
|         }
 | |
|     }
 | |
|     umtx_unlock(&gTZDBNamesMapLock);
 | |
| 
 | |
|     return tzdbNames;
 | |
| }
 | |
| 
 | |
| U_NAMESPACE_END
 | |
| 
 | |
| 
 | |
| #endif /* #if !UCONFIG_NO_FORMATTING */
 | |
| 
 | |
| //eof
 |