]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/tznames_impl.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
index 1595d89c549472c01f56da3e3aa7bc63226b7a01..3ebbc115594eb71a1f8c5ffc0d758c274b1e62da 100644 (file)
@@ -1,6 +1,8 @@
+// © 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"
@@ -28,7 +33,6 @@
 #include "uvector.h"
 #include "olsontz.h"
 
-
 U_NAMESPACE_BEGIN
 
 #define ZID_KEY_MAX  128
@@ -37,25 +41,18 @@ 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() {
+    static UMutex *m = STATIC_NEW(UMutex);
+    return m;
+}
 
 static UHashtable* gTZDBNamesMap = NULL;
 static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
@@ -63,6 +60,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* 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) {
@@ -81,6 +92,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 +213,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 +225,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 +249,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;
     }
@@ -313,8 +359,6 @@ TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
     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
@@ -342,7 +386,11 @@ TextTrieMap::search(const UnicodeString &text, int32_t start,
         //       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);
@@ -368,25 +416,29 @@ TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t star
             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);
     }
 }
 
@@ -517,196 +569,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;
-    }
-    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
@@ -772,25 +911,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 +1003,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 +1017,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 +1053,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 +1072,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)) != NULL) && U_SUCCESS(status)) {
+        loadMetaZoneNames(*mzID, status);
     }
 }
 
@@ -1096,7 +1201,6 @@ TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* re
     return tzID;
 }
 
-
 UnicodeString&
 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
                                           UTimeZoneNameType type,
@@ -1109,11 +1213,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 +1236,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 +1259,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);
@@ -1182,7 +1289,7 @@ static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
 
     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);
@@ -1193,215 +1300,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;
-    }
-
-    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/
@@ -1554,7 +1832,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);
         }
@@ -1670,8 +1948,8 @@ TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *nod
                     // 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;
@@ -1785,8 +2063,11 @@ static void U_CALLCONV prepareFind(UErrorCode &status) {
     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;
             }
@@ -1851,7 +2132,7 @@ TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
 : 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];
@@ -1918,9 +2199,11 @@ TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
     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);
+            }
         }
     }
 
@@ -1964,7 +2247,8 @@ TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& statu
     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) {
@@ -1982,9 +2266,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;
@@ -2006,7 +2291,7 @@ TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& statu
             tzdbNames = (TZDBNames *)cacheVal;
         }
     }
-    umtx_unlock(&gTZDBNamesMapLock);
+    umtx_unlock(gTZDBNamesMapLock);
 
     return tzdbNames;
 }