summaryrefslogtreecommitdiff
path: root/intl/icu/source/i18n/tznames_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/tznames_impl.cpp')
-rw-r--r--intl/icu/source/i18n/tznames_impl.cpp1314
1 files changed, 1314 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/tznames_impl.cpp b/intl/icu/source/i18n/tznames_impl.cpp
new file mode 100644
index 0000000..876411b
--- /dev/null
+++ b/intl/icu/source/i18n/tznames_impl.cpp
@@ -0,0 +1,1314 @@
+/*
+*******************************************************************************
+* Copyright (C) 2011-2012, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File TZNAMES_IMPL.CPP
+*
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/ustring.h"
+#include "unicode/timezone.h"
+
+#include "tznames_impl.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "uassert.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* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"};
+static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
+
+static const char gEcTag[] = "ec";
+
+static const char EMPTY[] = "<empty>"; // place holder for empty ZNames/TZNames
+
+static const UTimeZoneNameType ALL_NAME_TYPES[] = {
+ UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT,
+ UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT,
+ UTZNM_UNKNOWN // unknown as the last one
+};
+
+#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->addElement(fValues, status);
+ fValues = values;
+ fHasValuesVector = TRUE;
+ }
+ // Add the new value.
+ ((UVector *)fValues)->addElement(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 for 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)) {
+ return;
+ }
+ U_ASSERT(fLazyContents != NULL);
+ UChar *s = const_cast<UChar *>(key);
+ fLazyContents->addElement(s, status);
+ fLazyContents->addElement(value, status);
+}
+
+void
+TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
+ if (fNodes == NULL) {
+ fNodesCapacity = 512;
+ fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
+ 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;
+}
+
+// Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
+static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
+
+// 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) {
+ umtx_lock(&TextTrieMutex);
+ 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;
+ }
+ umtx_unlock(&TextTrieMutex);
+}
+
+void
+TextTrieMap::search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ UBool trieNeedsInitialization = FALSE;
+ UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
+ if (trieNeedsInitialization) {
+ 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;
+ }
+ }
+ UChar32 c = text.char32At(index);
+ if (fIgnoreCase) {
+ // size of character may grow after fold operation
+ UnicodeString tmp(c);
+ tmp.foldCase();
+ int32_t tmpidx = 0;
+ while (tmpidx < tmp.length()) {
+ c = tmp.char32At(tmpidx);
+ node = getChildNode(node, c);
+ if (node == NULL) {
+ break;
+ }
+ tmpidx = tmp.moveIndex32(tmpidx, 1);
+ }
+ } else {
+ node = getChildNode(node, c);
+ }
+ if (node != NULL) {
+ search(node, text, start, index+1, 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 perisist 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;
+}
+
+
+// ---------------------------------------------------
+// ZNames - names common for time zone and meta zone
+// ---------------------------------------------------
+class ZNames : public UMemory {
+public:
+ virtual ~ZNames();
+
+ static ZNames* createInstance(UResourceBundle* rb, const char* key);
+ const UChar* getName(UTimeZoneNameType type);
+
+protected:
+ ZNames(const UChar** names);
+ static const UChar** loadData(UResourceBundle* rb, const char* key);
+
+private:
+ const UChar** fNames;
+};
+
+ZNames::ZNames(const UChar** names)
+: fNames(names) {
+}
+
+ZNames::~ZNames() {
+ if (fNames != NULL) {
+ uprv_free(fNames);
+ }
+}
+
+ZNames*
+ZNames::createInstance(UResourceBundle* rb, const char* key) {
+ const UChar** names = loadData(rb, key);
+ if (names == NULL) {
+ // No names data available
+ return NULL;
+ }
+ return new ZNames(names);
+}
+
+const UChar*
+ZNames::getName(UTimeZoneNameType type) {
+ if (fNames == NULL) {
+ return NULL;
+ }
+ const UChar *name = NULL;
+ switch(type) {
+ case UTZNM_LONG_GENERIC:
+ name = fNames[0];
+ break;
+ case UTZNM_LONG_STANDARD:
+ name = fNames[1];
+ break;
+ case UTZNM_LONG_DAYLIGHT:
+ name = fNames[2];
+ break;
+ case UTZNM_SHORT_GENERIC:
+ name = fNames[3];
+ break;
+ case UTZNM_SHORT_STANDARD:
+ name = fNames[4];
+ break;
+ case UTZNM_SHORT_DAYLIGHT:
+ name = fNames[5];
+ break;
+ default:
+ name = NULL;
+ }
+ return name;
+}
+
+const UChar**
+ZNames::loadData(UResourceBundle* rb, const char* key) {
+ if (rb == NULL || key == NULL || *key == 0) {
+ return NULL;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ const UChar **names = NULL;
+
+ UResourceBundle* rbTable = NULL;
+ rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status);
+ if (U_SUCCESS(status)) {
+ names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE);
+ if (names != NULL) {
+ UBool isEmpty = TRUE;
+ for (int32_t i = 0; i < KEYS_SIZE; i++) {
+ status = U_ZERO_ERROR;
+ int32_t len = 0;
+ const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status);
+ if (U_FAILURE(status) || len == 0) {
+ names[i] = NULL;
+ } else {
+ names[i] = value;
+ isEmpty = FALSE;
+ }
+ }
+ if (isEmpty) {
+ // No need to keep the names array
+ uprv_free(names);
+ names = NULL;
+ }
+ }
+ }
+ ures_close(rbTable);
+ return names;
+}
+
+// ---------------------------------------------------
+// TZNames - names for a time zone
+// ---------------------------------------------------
+class TZNames : public ZNames {
+public:
+ virtual ~TZNames();
+
+ static TZNames* createInstance(UResourceBundle* rb, const char* key);
+ const UChar* getLocationName(void);
+
+private:
+ TZNames(const UChar** names, const UChar* locationName);
+ const UChar* fLocationName;
+};
+
+TZNames::TZNames(const UChar** names, const UChar* locationName)
+: ZNames(names), fLocationName(locationName) {
+}
+
+TZNames::~TZNames() {
+}
+
+const UChar*
+TZNames::getLocationName() {
+ return fLocationName;
+}
+
+TZNames*
+TZNames::createInstance(UResourceBundle* rb, const char* key) {
+ if (rb == NULL || key == NULL || *key == 0) {
+ return NULL;
+ }
+ TZNames* tznames = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle* rbTable = ures_getByKeyWithFallback(rb, key, NULL, &status);
+ if (U_SUCCESS(status)) {
+ int32_t len = 0;
+ const UChar* locationName = ures_getStringByKeyWithFallback(rbTable, gEcTag, &len, &status);
+ if (U_FAILURE(status) || len == 0) {
+ locationName = NULL;
+ }
+
+ const UChar** names = loadData(rb, key);
+
+ if (locationName != NULL || names != NULL) {
+ tznames = new TZNames(names, locationName);
+ }
+ }
+ ures_close(rbTable);
+ return tznames;
+}
+
+// ---------------------------------------------------
+// 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;
+ virtual const UnicodeString* snext(UErrorCode& status);
+ virtual void reset(UErrorCode& status);
+ virtual int32_t count(UErrorCode& status) const;
+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;
+ }
+}
+
+U_CDECL_BEGIN
+/**
+ * ZNameInfo stores zone name information in the trie
+ */
+typedef struct ZNameInfo {
+ UTimeZoneNameType type;
+ const UChar* tzID;
+ const UChar* mzID;
+} ZNameInfo;
+
+/**
+ * ZMatchInfo stores zone name match information used by find method
+ */
+typedef struct ZMatchInfo {
+ const ZNameInfo* znameInfo;
+ int32_t matchLength;
+} ZMatchInfo;
+U_CDECL_END
+
+
+// ---------------------------------------------------
+// ZNameSearchHandler
+// ---------------------------------------------------
+class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
+public:
+ ZNameSearchHandler(uint32_t types);
+ virtual ~ZNameSearchHandler();
+
+ UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
+ 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) {
+ break;
+ }
+ 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 TZNames
+ */
+static void U_CALLCONV
+deleteTZNames(void *obj) {
+ if (obj != EMPTY) {
+ delete (TZNames *)obj;
+ }
+}
+
+/**
+ * Deleter for ZNameInfo
+ */
+static void U_CALLCONV
+deleteZNameInfo(void *obj) {
+ uprv_free(obj);
+}
+
+U_CDECL_END
+
+static UMutex gLock = U_MUTEX_INITIALIZER;
+
+TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
+: fLocale(locale),
+ fZoneStrings(NULL),
+ fTZNamesMap(NULL),
+ fMZNamesMap(NULL),
+ fNamesTrieFullyLoaded(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, deleteTZNames);
+ // 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));
+ }
+ delete tz;
+
+ return;
+}
+
+/*
+ * This method updates the cache and must be called with a lock,
+ * except initializer.
+ */
+void
+TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
+ loadTimeZoneNames(tzCanonicalID);
+
+ UErrorCode status = U_ZERO_ERROR;
+ StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status);
+ if (U_SUCCESS(status) && mzIDs != NULL) {
+ const UnicodeString *mzID;
+ while ((mzID = mzIDs->snext(status))) {
+ if (U_FAILURE(status)) {
+ break;
+ }
+ loadMetaZoneNames(*mzID);
+ }
+ delete mzIDs;
+ }
+}
+
+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;
+ }
+}
+
+UBool
+TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
+ if (this == &other) {
+ return TRUE;
+ }
+ // No implementation for now
+ return FALSE;
+}
+
+TimeZoneNames*
+TimeZoneNamesImpl::clone() const {
+ UErrorCode status = U_ZERO_ERROR;
+ return new TimeZoneNamesImpl(fLocale, status);
+}
+
+StringEnumeration*
+TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
+ 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 {
+ 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->addElement((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 {
+ ZoneMeta::getMetazoneID(tzID, date, mzID);
+ return mzID;
+}
+
+UnicodeString&
+TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
+ 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);
+
+ umtx_lock(&gLock);
+ {
+ znames = nonConstThis->loadMetaZoneNames(mzID);
+ }
+ umtx_unlock(&gLock);
+
+ 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;
+ }
+
+ TZNames *tznames = NULL;
+ TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
+
+ umtx_lock(&gLock);
+ {
+ tznames = nonConstThis->loadTimeZoneNames(tzID);
+ }
+ umtx_unlock(&gLock);
+
+ 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 {
+ const UChar* locName = NULL;
+ TZNames *tznames = NULL;
+ TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
+
+ umtx_lock(&gLock);
+ {
+ tznames = nonConstThis->loadTimeZoneNames(tzID);
+ }
+ umtx_unlock(&gLock);
+
+ if (tznames != NULL) {
+ locName = tznames->getLocationName();
+ }
+ if (locName != NULL) {
+ name.setTo(TRUE, locName, -1);
+ return name;
+ }
+
+ return TimeZoneNames::getExemplarLocationName(tzID, 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 = 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) {
+ if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) {
+ return NULL;
+ }
+
+ ZNames *znames = NULL;
+
+ UErrorCode status = U_ZERO_ERROR;
+ 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;
+
+ void *cacheVal = uhash_get(fMZNamesMap, mzIDKey);
+ if (cacheVal == NULL) {
+ char key[ZID_KEY_MAX + 1];
+ mergeTimeZoneKey(mzID, key);
+ znames = ZNames::createInstance(fZoneStrings, key);
+
+ if (znames == NULL) {
+ cacheVal = (void *)EMPTY;
+ } else {
+ cacheVal = znames;
+ }
+ // Use the persistent ID as the resource key, so we can
+ // avoid duplications.
+ const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
+ if (newKey != NULL) {
+ uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status);
+ if (U_FAILURE(status)) {
+ if (znames != NULL) {
+ delete znames;
+ }
+ } else if (znames != NULL) {
+ // put the name info into the trie
+ for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
+ const UChar* name = znames->getName(ALL_NAME_TYPES[i]);
+ if (name != NULL) {
+ ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
+ if (nameinfo != NULL) {
+ nameinfo->type = ALL_NAME_TYPES[i];
+ nameinfo->tzID = NULL;
+ nameinfo->mzID = newKey;
+ fNamesTrie.put(name, nameinfo, status);
+ }
+ }
+ }
+ }
+
+ } else {
+ // Should never happen with a valid input
+ if (znames != NULL) {
+ // It's not possible that we get a valid ZNames with unknown ID.
+ // But just in case..
+ delete znames;
+ znames = NULL;
+ }
+ }
+ } else if (cacheVal != EMPTY) {
+ znames = (ZNames *)cacheVal;
+ }
+
+ return znames;
+}
+
+/*
+ * This method updates the cache and must be called with a lock
+ */
+TZNames*
+TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
+ if (tzID.length() > ZID_KEY_MAX) {
+ return NULL;
+ }
+
+ TZNames *tznames = NULL;
+
+ UErrorCode status = U_ZERO_ERROR;
+ UChar tzIDKey[ZID_KEY_MAX + 1];
+ int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
+ U_ASSERT(status == U_ZERO_ERROR); // already checked length above
+ tzIDKey[tzIDKeyLen] = 0;
+
+ void *cacheVal = uhash_get(fTZNamesMap, tzIDKey);
+ if (cacheVal == NULL) {
+ char key[ZID_KEY_MAX + 1];
+ UErrorCode status = U_ZERO_ERROR;
+ // 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);
+ }
+ }
+ uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
+ tznames = TZNames::createInstance(fZoneStrings, key);
+
+ if (tznames == NULL) {
+ cacheVal = (void *)EMPTY;
+ } else {
+ cacheVal = tznames;
+ }
+ // Use the persistent ID as the resource key, so we can
+ // avoid duplications.
+ const UChar* newKey = ZoneMeta::findTimeZoneID(tzID);
+ if (newKey != NULL) {
+ uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status);
+ if (U_FAILURE(status)) {
+ if (tznames != NULL) {
+ delete tznames;
+ }
+ } else if (tznames != NULL) {
+ // put the name info into the trie
+ for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
+ const UChar* name = tznames->getName(ALL_NAME_TYPES[i]);
+ if (name != NULL) {
+ ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
+ if (nameinfo != NULL) {
+ nameinfo->type = ALL_NAME_TYPES[i];
+ nameinfo->tzID = newKey;
+ nameinfo->mzID = NULL;
+ fNamesTrie.put(name, nameinfo, status);
+ }
+ }
+ }
+ }
+ } else {
+ // Should never happen with a valid input
+ if (tznames != NULL) {
+ // It's not possible that we get a valid TZNames with unknown ID.
+ // But just in case..
+ delete tznames;
+ tznames = NULL;
+ }
+ }
+ } else if (cacheVal != EMPTY) {
+ tznames = (TZNames *)cacheVal;
+ }
+
+ return tznames;
+}
+
+TimeZoneNames::MatchInfoCollection*
+TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
+ ZNameSearchHandler handler(types);
+
+ TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
+
+ umtx_lock(&gLock);
+ {
+ fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+ }
+ umtx_unlock(&gLock);
+
+ 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
+ return matches;
+ }
+
+ delete matches;
+
+ // All names are not yet loaded into the trie
+ umtx_lock(&gLock);
+ {
+ if (!fNamesTrieFullyLoaded) {
+ 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))) {
+ if (U_FAILURE(status)) {
+ break;
+ }
+ // loadStrings also load related metazone strings
+ nonConstThis->loadStrings(*id);
+ }
+ }
+ if (tzIDs != NULL) {
+ delete tzIDs;
+ }
+ if (U_SUCCESS(status)) {
+ nonConstThis->fNamesTrieFullyLoaded = TRUE;
+ }
+ }
+ }
+ umtx_unlock(&gLock);
+
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ umtx_lock(&gLock);
+ {
+ // now try it again
+ fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+ }
+ umtx_unlock(&gLock);
+
+ return handler.getMatches(maxLen);
+}
+
+U_NAMESPACE_END
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+//eof