| Index: source/i18n/tznames_impl.cpp
|
| diff --git a/source/i18n/tznames_impl.cpp b/source/i18n/tznames_impl.cpp
|
| index 1595d89c549472c01f56da3e3aa7bc63226b7a01..f52d5c23ac350f9eee6f752c1a21c93c71d680ad 100644
|
| --- a/source/i18n/tznames_impl.cpp
|
| +++ b/source/i18n/tznames_impl.cpp
|
| @@ -1,6 +1,8 @@
|
| +// Copyright (C) 2016 and later: Unicode, Inc. and others.
|
| +// License & terms of use: http://www.unicode.org/copyright.html
|
| /*
|
| *******************************************************************************
|
| -* Copyright (C) 2011-2014, International Business Machines Corporation and
|
| +* Copyright (C) 2011-2016, International Business Machines Corporation and
|
| * others. All Rights Reserved.
|
| *******************************************************************************
|
| *
|
| @@ -21,6 +23,7 @@
|
| #include "cstring.h"
|
| #include "uassert.h"
|
| #include "mutex.h"
|
| +#include "resource.h"
|
| #include "uresimp.h"
|
| #include "ureslocs.h"
|
| #include "zonemeta.h"
|
| @@ -28,7 +31,6 @@
|
| #include "uvector.h"
|
| #include "olsontz.h"
|
|
|
| -
|
| U_NAMESPACE_BEGIN
|
|
|
| #define ZID_KEY_MAX 128
|
| @@ -37,25 +39,16 @@ U_NAMESPACE_BEGIN
|
| 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_EXEMPLAR_LOCATION,
|
| - UTZNM_UNKNOWN // unknown as the last one
|
| -};
|
| +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 = (sizeof TZDBNAMES_KEYS / sizeof TZDBNAMES_KEYS[0]);
|
| +static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
|
|
|
| static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER;
|
| +static UMutex gDataMutex = U_MUTEX_INITIALIZER;
|
|
|
| static UHashtable* gTZDBNamesMap = NULL;
|
| static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
|
| @@ -63,6 +56,20 @@ 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* 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) {
|
| @@ -81,6 +88,26 @@ static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) {
|
| }
|
| 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
|
|
|
| // ---------------------------------------------------
|
| @@ -182,7 +209,7 @@ TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UError
|
| put(s, value, status);
|
| }
|
|
|
| -// This method is for designed for a persistent key, such as string key stored in
|
| +// 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) {
|
| @@ -194,11 +221,22 @@ TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
|
| }
|
| }
|
| if (U_FAILURE(status)) {
|
| + if (fValueDeleter) {
|
| + fValueDeleter((void*) key);
|
| + }
|
| return;
|
| }
|
| U_ASSERT(fLazyContents != NULL);
|
| +
|
| UChar *s = const_cast<UChar *>(key);
|
| fLazyContents->addElement(s, status);
|
| + if (U_FAILURE(status)) {
|
| + if (fValueDeleter) {
|
| + fValueDeleter((void*) key);
|
| + }
|
| + return;
|
| + }
|
| +
|
| fLazyContents->addElement(value, status);
|
| }
|
|
|
| @@ -207,6 +245,10 @@ 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;
|
| }
|
| @@ -517,196 +559,283 @@ void ZNStringPool::freeze() {
|
| }
|
|
|
|
|
| -// ---------------------------------------------------
|
| -// ZNames - names common for time zone and meta zone
|
| -// ---------------------------------------------------
|
| +/**
|
| + * This class stores name data for a meta zone or time zone.
|
| + */
|
| class ZNames : public UMemory {
|
| -public:
|
| - virtual ~ZNames();
|
| +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;
|
| + }
|
| + }
|
|
|
| - static ZNames* createInstance(UResourceBundle* rb, const char* key);
|
| - virtual const UChar* getName(UTimeZoneNameType type);
|
| + const UChar* fNames[UTZNM_INDEX_COUNT];
|
| + UBool fDidAddIntoTrie;
|
|
|
| -protected:
|
| - ZNames(const UChar** names);
|
| - static const UChar** loadData(UResourceBundle* rb, const char* key);
|
| + // 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:
|
| - const UChar** fNames;
|
| -};
|
| + static void* createMetaZoneAndPutInCache(UHashtable* cache, const UChar* names[],
|
| + const UnicodeString& mzID, UErrorCode& status) {
|
| + if (U_FAILURE(status)) { return NULL; }
|
| + U_ASSERT(names != NULL);
|
|
|
| -ZNames::ZNames(const UChar** names)
|
| -: fNames(names) {
|
| -}
|
| + // 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);
|
| + }
|
| + }
|
|
|
| -ZNames::~ZNames() {
|
| - if (fNames != NULL) {
|
| - uprv_free(fNames);
|
| + // 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;
|
| }
|
| -}
|
|
|
| -ZNames*
|
| -ZNames::createInstance(UResourceBundle* rb, const char* key) {
|
| - const UChar** names = loadData(rb, key);
|
| - if (names == NULL) {
|
| - // No names data available
|
| - return NULL;
|
| + const UChar* getName(UTimeZoneNameType type) const {
|
| + UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
|
| + return index >= 0 ? fNames[index] : NULL;
|
| }
|
| - return new ZNames(names);
|
| -}
|
|
|
| -const UChar*
|
| -ZNames::getName(UTimeZoneNameType type) {
|
| - if (fNames == NULL) {
|
| - return NULL;
|
| + void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
|
| + addNamesIntoTrie(mzID, NULL, trie, status);
|
| }
|
| - 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;
|
| - case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass
|
| - default:
|
| - name = NULL;
|
| - }
|
| - return name;
|
| -}
|
| -
|
| -const UChar**
|
| -ZNames::loadData(UResourceBundle* rb, const char* key) {
|
| - if (rb == NULL || key == NULL || *key == 0) {
|
| - return NULL;
|
| + void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) {
|
| + addNamesIntoTrie(NULL, tzID, trie, status);
|
| }
|
|
|
| - UErrorCode status = U_ZERO_ERROR;
|
| - const UChar **names = NULL;
|
| + void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie,
|
| + UErrorCode& status) {
|
| + if (U_FAILURE(status)) { return; }
|
| + if (fDidAddIntoTrie) { return; }
|
| + fDidAddIntoTrie = TRUE;
|
|
|
| - 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;
|
| + 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;
|
| }
|
| - }
|
| - 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 UnicodeString& tzID);
|
| - virtual const UChar* getName(UTimeZoneNameType type);
|
| -
|
| -private:
|
| - TZNames(const UChar** names);
|
| - const UChar* fLocationName;
|
| - UChar* fLocationNameOwned;
|
| + struct ZNamesLoader;
|
| };
|
|
|
| -TZNames::TZNames(const UChar** names)
|
| -: ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) {
|
| -}
|
| +struct ZNames::ZNamesLoader : public ResourceSink {
|
| + const UChar *names[UTZNM_INDEX_COUNT];
|
|
|
| -TZNames::~TZNames() {
|
| - if (fLocationNameOwned) {
|
| - uprv_free(fLocationNameOwned);
|
| + ZNamesLoader() {
|
| + clear();
|
| }
|
| -}
|
| + virtual ~ZNamesLoader();
|
|
|
| -const UChar*
|
| -TZNames::getName(UTimeZoneNameType type) {
|
| - if (type == UTZNM_EXEMPLAR_LOCATION) {
|
| - return fLocationName;
|
| + /** Reset for loading another set of names. */
|
| + void clear() {
|
| + uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
|
| }
|
| - return ZNames::getName(type);
|
| -}
|
|
|
| -TZNames*
|
| -TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) {
|
| - if (rb == NULL || key == NULL || *key == 0) {
|
| - return NULL;
|
| + 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);
|
| }
|
|
|
| - const UChar** names = loadData(rb, key);
|
| - const UChar* locationName = NULL;
|
| - UChar* locationNameOwned = NULL;
|
| + 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);
|
| + }
|
| + }
|
|
|
| - UErrorCode status = U_ZERO_ERROR;
|
| - int32_t len = 0;
|
| + char key[ZID_KEY_MAX + 1];
|
| + uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
|
|
|
| - UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status);
|
| - locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status);
|
| - // ignore missing resource here
|
| - status = U_ZERO_ERROR;
|
| + loadNames(zoneStrings, key, errorCode);
|
| + }
|
|
|
| - ures_close(table);
|
| + void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
|
| + U_ASSERT(zoneStrings != NULL);
|
| + U_ASSERT(key != NULL);
|
| + U_ASSERT(key[0] != '\0');
|
|
|
| - if (locationName == NULL) {
|
| - UnicodeString tmpName;
|
| - int32_t tmpNameLen = 0;
|
| - TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName);
|
| - tmpNameLen = tmpName.length();
|
| + UErrorCode localStatus = U_ZERO_ERROR;
|
| + clear();
|
| + ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
|
|
|
| - if (tmpNameLen > 0) {
|
| - locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1));
|
| - if (locationNameOwned) {
|
| - tmpName.extract(locationNameOwned, tmpNameLen + 1, status);
|
| - locationName = locationNameOwned;
|
| + // Ignore errors, but propogate 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) {
|
| + 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);
|
| }
|
| }
|
| }
|
|
|
| - TZNames* tznames = NULL;
|
| - if (locationName != NULL || names != NULL) {
|
| - tznames = new TZNames(names);
|
| - if (tznames == NULL) {
|
| - if (locationNameOwned) {
|
| - uprv_free(locationNameOwned);
|
| + 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;
|
| }
|
| }
|
| - tznames->fLocationName = locationName;
|
| - tznames->fLocationNameOwned = locationNameOwned;
|
| + return names;
|
| }
|
| +};
|
| +
|
| +ZNames::ZNamesLoader::~ZNamesLoader() {}
|
|
|
| - return tznames;
|
| -}
|
|
|
| // ---------------------------------------------------
|
| // The meta zone ID enumeration class
|
| @@ -772,25 +901,6 @@ MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
|
| }
|
| }
|
|
|
| -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
|
| @@ -883,16 +993,7 @@ U_CDECL_BEGIN
|
| 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;
|
| + delete (ZNames*) obj;
|
| }
|
| }
|
|
|
| @@ -906,14 +1007,13 @@ deleteZNameInfo(void *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),
|
| + fNamesFullyLoaded(FALSE),
|
| fNamesTrie(TRUE, deleteZNameInfo) {
|
| initialize(locale, status);
|
| }
|
| @@ -943,14 +1043,14 @@ TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
|
| }
|
|
|
| uhash_setValueDeleter(fMZNamesMap, deleteZNames);
|
| - uhash_setValueDeleter(fTZNamesMap, deleteTZNames);
|
| + 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));
|
| + loadStrings(UnicodeString(tzID), status);
|
| }
|
| delete tz;
|
|
|
| @@ -962,20 +1062,15 @@ TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
|
| * except initializer.
|
| */
|
| void
|
| -TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
|
| - loadTimeZoneNames(tzCanonicalID);
|
| +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());
|
|
|
| - 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;
|
| + const UnicodeString *mzID;
|
| + while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
|
| + loadMetaZoneNames(*mzID, status);
|
| }
|
| }
|
|
|
| @@ -1096,7 +1191,6 @@ TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* re
|
| return tzID;
|
| }
|
|
|
| -
|
| UnicodeString&
|
| TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
|
| UTimeZoneNameType type,
|
| @@ -1109,11 +1203,12 @@ TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
|
| ZNames *znames = NULL;
|
| TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
|
|
|
| - umtx_lock(&gLock);
|
| {
|
| - znames = nonConstThis->loadMetaZoneNames(mzID);
|
| + Mutex lock(&gDataMutex);
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + znames = nonConstThis->loadMetaZoneNames(mzID, status);
|
| + if (U_FAILURE(status)) { return name; }
|
| }
|
| - umtx_unlock(&gLock);
|
|
|
| if (znames != NULL) {
|
| const UChar* s = znames->getName(type);
|
| @@ -1131,14 +1226,15 @@ TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNa
|
| return name;
|
| }
|
|
|
| - TZNames *tznames = NULL;
|
| + ZNames *tznames = NULL;
|
| TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
|
|
|
| - umtx_lock(&gLock);
|
| {
|
| - tznames = nonConstThis->loadTimeZoneNames(tzID);
|
| + Mutex lock(&gDataMutex);
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + tznames = nonConstThis->loadTimeZoneNames(tzID, status);
|
| + if (U_FAILURE(status)) { return name; }
|
| }
|
| - umtx_unlock(&gLock);
|
|
|
| if (tznames != NULL) {
|
| const UChar *s = tznames->getName(type);
|
| @@ -1153,14 +1249,15 @@ UnicodeString&
|
| TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
|
| name.setToBogus(); // cleanup result.
|
| const UChar* locName = NULL;
|
| - TZNames *tznames = NULL;
|
| + ZNames *tznames = NULL;
|
| TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
|
|
|
| - umtx_lock(&gLock);
|
| {
|
| - tznames = nonConstThis->loadTimeZoneNames(tzID);
|
| + Mutex lock(&gDataMutex);
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + tznames = nonConstThis->loadTimeZoneNames(tzID, status);
|
| + if (U_FAILURE(status)) { return name; }
|
| }
|
| - umtx_unlock(&gLock);
|
|
|
| if (tznames != NULL) {
|
| locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
|
| @@ -1193,215 +1290,386 @@ static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
|
| * 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;
|
| - }
|
| +TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
|
| + if (U_FAILURE(status)) { return NULL; }
|
| + U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
|
|
|
| - 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
|
| + U_ASSERT(U_SUCCESS(status)); // 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;
|
| - znames = NULL;
|
| - }
|
| - } 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;
|
| + 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; }
|
| }
|
|
|
| - return znames;
|
| + if (mznames != EMPTY) {
|
| + return (ZNames*)mznames;
|
| + } else {
|
| + return NULL;
|
| + }
|
| }
|
|
|
| /*
|
| * 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;
|
| +ZNames*
|
| +TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
|
| + if (U_FAILURE(status)) { return NULL; }
|
| + U_ASSERT(tzID.length() <= ZID_KEY_MAX);
|
|
|
| - 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
|
| + U_ASSERT(U_SUCCESS(status)); // 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, tzID);
|
| -
|
| - 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;
|
| - tznames = NULL;
|
| - }
|
| - } 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;
|
| + 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; }
|
| }
|
|
|
| - return tznames;
|
| + // 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);
|
|
|
| - TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
|
| -
|
| - umtx_lock(&gLock);
|
| + // Synchronize so that data is not loaded multiple times.
|
| + // TODO: Consider more fine-grained synchronization.
|
| {
|
| - fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
|
| - }
|
| - umtx_unlock(&gLock);
|
| + Mutex lock(&gDataMutex);
|
|
|
| - if (U_FAILURE(status)) {
|
| - return NULL;
|
| + // 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
|
| + // perfect match, or no more names available
|
| return matches;
|
| }
|
| -
|
| delete matches;
|
| + return NULL;
|
| +}
|
|
|
| - // All names are not yet loaded into the trie
|
| - umtx_lock(&gLock);
|
| - {
|
| - if (!fNamesTrieFullyLoaded) {
|
| - const UnicodeString *id;
|
| +// Caller must synchronize.
|
| +void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
|
| + if (U_FAILURE(status)) return;
|
| + int32_t pos;
|
| + const UHashElement* element;
|
|
|
| - // 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;
|
| + 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) * (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, 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;
|
| }
|
| - // loadStrings also load related metazone strings
|
| - nonConstThis->loadStrings(*id);
|
| }
|
| }
|
| - if (tzIDs != NULL) {
|
| - delete tzIDs;
|
| +
|
| + void* newKey = createKey(key, status);
|
| + if (U_FAILURE(status)) {
|
| + deleteZNamesLoader(loader);
|
| + return;
|
| }
|
| - if (U_SUCCESS(status)) {
|
| - nonConstThis->fNamesTrieFullyLoaded = TRUE;
|
| +
|
| + 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) {
|
| + 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; }
|
| }
|
| }
|
| - umtx_unlock(&gLock);
|
| +};
|
|
|
| - if (U_FAILURE(status)) {
|
| - return NULL;
|
| +// 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);
|
|
|
| - umtx_lock(&gLock);
|
| + // Load the time zone strings
|
| {
|
| - // now try it again
|
| - fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
|
| + 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();
|
| + }
|
| }
|
| - umtx_unlock(&gLock);
|
| +}
|
|
|
| - return handler.getMatches(maxLen);
|
| +// 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))) {
|
| + 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/
|
| @@ -1554,7 +1822,7 @@ TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
|
| if (regions != NULL) {
|
| char **p = regions;
|
| for (int32_t i = 0; i < numRegions; p++, i++) {
|
| - uprv_free(p);
|
| + uprv_free(*p);
|
| }
|
| uprv_free(regions);
|
| }
|
| @@ -1982,9 +2250,10 @@ TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& statu
|
| }
|
| // Use the persistent ID as the resource key, so we can
|
| // avoid duplications.
|
| - const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
|
| + // TODO: Is there a more efficient way, like intern() in Java?
|
| + void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
|
| if (newKey != NULL) {
|
| - uhash_put(gTZDBNamesMap, (void *)newKey, cacheVal, &status);
|
| + uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
|
| if (U_FAILURE(status)) {
|
| if (tzdbNames != NULL) {
|
| delete tzdbNames;
|
|
|