+// © 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.
*******************************************************************************
*
#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 "cmemory.h"
#include "cstring.h"
#include "uassert.h"
#include "mutex.h"
+#include "resource.h"
#include "uresimp.h"
#include "ureslocs.h"
#include "zonemeta.h"
#include "uvector.h"
#include "olsontz.h"
-
U_NAMESPACE_BEGIN
#define ZID_KEY_MAX 128
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() {
+ static UMutex *m = STATIC_NEW(UMutex);
+ return m;
+}
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) {
}
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
// ---------------------------------------------------
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) {
}
}
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);
}
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;
}
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
// the ICU atomic safe functions for assigning and testing.
// Don't test the pointer fLazyContents.
// Don't do unless it's really required.
- Mutex lock(&TextTrieMutex);
+
+ // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
+ static UMutex *TextTrieMutex = STATIC_NEW(UMutex);
+
+ Mutex lock(TextTrieMutex);
if (fLazyContents != NULL) {
TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
nonConstThis->buildTrie(status);
return;
}
}
- UChar32 c = text.char32At(index);
if (fIgnoreCase) {
- // size of character may grow after fold operation
- UnicodeString tmp(c);
+ // 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()) {
- c = tmp.char32At(tmpidx);
+ UChar c = tmp.charAt(tmpidx++);
node = getChildNode(node, c);
if (node == NULL) {
break;
}
- tmpidx = tmp.moveIndex32(tmpidx, 1);
}
} 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+1, handler, status);
+ search(node, text, start, index, handler, status);
}
}
}
-// ---------------------------------------------------
-// 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;
- }
- 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;
+ void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
+ addNamesIntoTrie(mzID, NULL, trie, status);
}
- 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);
+ }
+ }
+
+ char key[ZID_KEY_MAX + 1];
+ uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
- UErrorCode status = U_ZERO_ERROR;
- int32_t len = 0;
+ loadNames(zoneStrings, key, errorCode);
+ }
- UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status);
- locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status);
- // ignore missing resource here
- status = U_ZERO_ERROR;
+ void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
+ U_ASSERT(zoneStrings != NULL);
+ U_ASSERT(key != NULL);
+ U_ASSERT(key[0] != '\0');
- ures_close(table);
+ UErrorCode localStatus = U_ZERO_ERROR;
+ clear();
+ ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
- if (locationName == NULL) {
- UnicodeString tmpName;
- int32_t tmpNameLen = 0;
- TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName);
- tmpNameLen = tmpName.length();
+ // 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);
+ }
+ }
- if (tmpNameLen > 0) {
- locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1));
- if (locationNameOwned) {
- tmpName.extract(locationNameOwned, tmpNameLen + 1, status);
- locationName = locationNameOwned;
+ 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
}
}
-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
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;
}
}
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);
}
}
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;
* 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)) != NULL) && U_SUCCESS(status)) {
+ loadMetaZoneNames(*mzID, status);
}
}
return tzID;
}
-
UnicodeString&
TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
UTimeZoneNameType type,
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);
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);
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);
char mzIdChar[ZID_KEY_MAX + 1];
int32_t keyLen;
- int32_t prefixLen = uprv_strlen(gMZPrefix);
+ 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);
* 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;
+TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
+ if (U_FAILURE(status)) { return NULL; }
+ U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
- 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) * (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;
}
- // 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)) != 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/
if (regions != NULL) {
char **p = regions;
for (int32_t i = 0; i < numRegions; p++, i++) {
- uprv_free(p);
+ uprv_free(*p);
}
uprv_free(regions);
}
// 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 i = 0; i < ninfo->nRegions; i++) {
- const char *region = ninfo->parseRegions[i];
+ 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;
const UnicodeString *mzID;
StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
if (U_SUCCESS(status)) {
- while ((mzID = mzIDs->snext(status)) && 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;
}
: fLocale(locale) {
UBool useWorld = TRUE;
const char* region = fLocale.getCountry();
- int32_t regionLen = uprv_strlen(region);
+ int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
if (regionLen == 0) {
UErrorCode status = U_ZERO_ERROR;
char loc[ULOC_FULLNAME_CAPACITY];
UErrorCode status = U_ZERO_ERROR;
const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
if (U_SUCCESS(status)) {
- const UChar *s = tzdbNames->getName(type);
- if (s != NULL) {
- name.setTo(TRUE, s, -1);
+ if (tzdbNames != NULL) {
+ const UChar *s = tzdbNames->getName(type);
+ if (s != NULL) {
+ name.setTo(TRUE, s, -1);
+ }
}
}
U_ASSERT(status == U_ZERO_ERROR); // already checked length above
mzIDKey[mzID.length()] = 0;
- umtx_lock(&gTZDBNamesMapLock);
+ static UMutex *gTZDBNamesMapLock = STATIC_NEW(UMutex);
+ umtx_lock(gTZDBNamesMapLock);
{
void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
if (cacheVal == NULL) {
}
// 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;
tzdbNames = (TZDBNames *)cacheVal;
}
}
- umtx_unlock(&gTZDBNamesMapLock);
+ umtx_unlock(gTZDBNamesMapLock);
return tzdbNames;
}