diff --git a/icu4c/source/i18n/dtfmtsym.cpp b/icu4c/source/i18n/dtfmtsym.cpp index 2d9c683caf..3e0edbad50 100644 --- a/icu4c/source/i18n/dtfmtsym.cpp +++ b/icu4c/source/i18n/dtfmtsym.cpp @@ -1273,6 +1273,12 @@ DateFormatSymbols::initZoneStringsArray(void) { TimeZoneNames *tzNames = NULL; int32_t rows = 0; + static const UTimeZoneNameType TYPES[] = { + UTZNM_LONG_STANDARD, UTZNM_SHORT_STANDARD, + UTZNM_LONG_DAYLIGHT, UTZNM_SHORT_DAYLIGHT + }; + static const int32_t NUM_TYPES = 4; + do { // dummy do-while tzids = TimeZone::createTimeZoneIDEnumeration(ZONE_SET, NULL, NULL, status); @@ -1291,6 +1297,8 @@ DateFormatSymbols::initZoneStringsArray(void) { uprv_memset(zarray, 0, size); tzNames = TimeZoneNames::createInstance(fZSFLocale, status); + tzNames->loadAllDisplayNames(status); + if (U_FAILURE(status)) { break; } const UnicodeString *tzid; int32_t i = 0; @@ -1309,10 +1317,7 @@ DateFormatSymbols::initZoneStringsArray(void) { } zarray[i][0].setTo(*tzid); - zarray[i][1].setTo(tzNames->getDisplayName(*tzid, UTZNM_LONG_STANDARD, now, tzDispName)); - zarray[i][2].setTo(tzNames->getDisplayName(*tzid, UTZNM_SHORT_STANDARD, now, tzDispName)); - zarray[i][3].setTo(tzNames->getDisplayName(*tzid, UTZNM_LONG_DAYLIGHT, now, tzDispName)); - zarray[i][4].setTo(tzNames->getDisplayName(*tzid, UTZNM_SHORT_DAYLIGHT, now, tzDispName)); + tzNames->getDisplayNames(*tzid, TYPES, NUM_TYPES, now, zarray[i]+1); i++; } @@ -1338,7 +1343,7 @@ DateFormatSymbols::initZoneStringsArray(void) { fLocaleZoneStrings = zarray; fZoneStringsRowCount = rows; - fZoneStringsColCount = 5; + fZoneStringsColCount = 1 + NUM_TYPES; } void diff --git a/icu4c/source/i18n/tznames.cpp b/icu4c/source/i18n/tznames.cpp index fd35dcb198..660621f314 100644 --- a/icu4c/source/i18n/tznames.cpp +++ b/icu4c/source/i18n/tznames.cpp @@ -118,6 +118,8 @@ public: UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const; + void loadAllDisplayNames(UErrorCode& status); + MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; private: TimeZoneNamesDelegate(); @@ -280,6 +282,11 @@ TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, Unicod return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name); } +void +TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) { + fTZnamesCacheEntry->names->loadAllDisplayNames(status); +} + TimeZoneNames::MatchInfoCollection* TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { return fTZnamesCacheEntry->names->find(text, start, types, status); @@ -332,6 +339,29 @@ TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, return name; } +void +TimeZoneNames::loadAllDisplayNames(UErrorCode& status) { + return loadAllDisplayNames(status); +} + +void +TimeZoneNames::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[]) const { + if (tzID.isEmpty()) { return; } + UnicodeString mzID; + for (int i = 0; i < numTypes; i++) { + UnicodeString name; + UTimeZoneNameType type = types[i]; + getTimeZoneDisplayName(tzID, type, name); + if (name.isEmpty()) { + if (mzID.isEmpty()) { + getMetaZoneID(tzID, date, mzID); + } + getMetaZoneDisplayName(mzID, type, name); + } + dest[i].setTo(name); + } +} + struct MatchInfo : UMemory { UTimeZoneNameType nameType; diff --git a/icu4c/source/i18n/tznames_impl.cpp b/icu4c/source/i18n/tznames_impl.cpp index 4a1ff316d6..6b3cb540e4 100644 --- a/icu4c/source/i18n/tznames_impl.cpp +++ b/icu4c/source/i18n/tznames_impl.cpp @@ -23,6 +23,7 @@ #include "cstring.h" #include "uassert.h" #include "mutex.h" +#include "resource.h" #include "uresimp.h" #include "ureslocs.h" #include "zonemeta.h" @@ -30,7 +31,6 @@ #include "uvector.h" #include "olsontz.h" - U_NAMESPACE_BEGIN #define ZID_KEY_MAX 128 @@ -39,25 +39,16 @@ U_NAMESPACE_BEGIN static const char gZoneStrings[] = "zoneStrings"; static const char gMZPrefix[] = "meta:"; -static const char* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"}; -static const int32_t KEYS_SIZE = UPRV_LENGTHOF(KEYS); - -static const char gEcTag[] = "ec"; - -static const char 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[] = ""; // place holder for empty ZNames +static const char DUMMY_LOADER[] = ""; // place holder for dummy ZNamesLoader +static const UChar NO_NAME[] = { 0 }; // for empty no-fallback time zone names // stuff for TZDBTimeZoneNames static const char* TZDBNAMES_KEYS[] = {"ss", "sd"}; static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS); static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER; +static UMutex gDataMutex = U_MUTEX_INITIALIZER; static UHashtable* gTZDBNamesMap = NULL; static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER; @@ -65,6 +56,20 @@ static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER; static TextTrieMap* gTZDBNamesTrie = NULL; static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER; +// The order in which strings are stored may be different than the order in the public enum. +enum UTimeZoneNameTypeIndex { + UTZNM_INDEX_UNKNOWN = -1, + UTZNM_INDEX_EXEMPLAR_LOCATION, + UTZNM_INDEX_LONG_GENERIC, + UTZNM_INDEX_LONG_STANDARD, + UTZNM_INDEX_LONG_DAYLIGHT, + UTZNM_INDEX_SHORT_GENERIC, + UTZNM_INDEX_SHORT_STANDARD, + UTZNM_INDEX_SHORT_DAYLIGHT, + UTZNM_INDEX_COUNT +}; +static const UChar* EMPTY_NAMES[UTZNM_INDEX_COUNT] = {0,0,0,0,0,0,0}; + U_CDECL_BEGIN static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) { if (gTZDBNamesMap != NULL) { @@ -83,6 +88,26 @@ static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) { } U_CDECL_END +/** + * ZNameInfo stores zone name information in the trie + */ +struct ZNameInfo { + UTimeZoneNameType type; + const UChar* tzID; + const UChar* mzID; +}; + +/** + * ZMatchInfo stores zone name match information used by find method + */ +struct ZMatchInfo { + const ZNameInfo* znameInfo; + int32_t matchLength; +}; + +// Helper functions +static void mergeTimeZoneKey(const UnicodeString& mzID, char* result); + #define DEFAULT_CHARACTERNODE_CAPACITY 1 // --------------------------------------------------- @@ -184,7 +209,7 @@ TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UError put(s, value, status); } -// This method is for designed for a persistent key, such as string key stored in +// This method is designed for a persistent key, such as string key stored in // resource bundle. void TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { @@ -196,11 +221,22 @@ TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { } } if (U_FAILURE(status)) { + if (fValueDeleter) { + fValueDeleter((void*) key); + } return; } U_ASSERT(fLazyContents != NULL); + UChar *s = const_cast(key); fLazyContents->addElement(s, status); + if (U_FAILURE(status)) { + if (fValueDeleter) { + fValueDeleter((void*) key); + } + return; + } + fLazyContents->addElement(value, status); } @@ -209,6 +245,10 @@ TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) if (fNodes == NULL) { fNodesCapacity = 512; fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); + if (fNodes == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } fNodes[0].clear(); // Init root node. fNodesCount = 1; } @@ -519,196 +559,283 @@ void ZNStringPool::freeze() { } -// --------------------------------------------------- -// ZNames - names common for time zone and meta zone -// --------------------------------------------------- +/** + * This class stores name data for a meta zone or time zone. + */ class ZNames : public UMemory { +private: + friend class TimeZoneNamesImpl; + + static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) { + switch(type) { + case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION; + case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC; + case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD; + case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT; + case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC; + case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD; + case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT; + default: return UTZNM_INDEX_UNKNOWN; + } + } + static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) { + switch(index) { + case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION; + case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC; + case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD; + case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT; + case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC; + case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD; + case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT; + default: return UTZNM_UNKNOWN; + } + } + + const UChar* fNames[UTZNM_INDEX_COUNT]; + UBool fDidAddIntoTrie; + + // Whether we own the location string, if computed rather than loaded from a bundle. + // A meta zone names instance never has an exemplar location string. + UBool fOwnsLocationName; + + ZNames(const UChar* names[], const UChar* locationName) + : fDidAddIntoTrie(FALSE) { + uprv_memcpy(fNames, names, sizeof(fNames)); + if (locationName != NULL) { + fOwnsLocationName = TRUE; + fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName; + } else { + fOwnsLocationName = FALSE; + } + } + public: - virtual ~ZNames(); - - static ZNames* createInstance(UResourceBundle* rb, const char* key); - virtual const UChar* getName(UTimeZoneNameType type); - -protected: - ZNames(const UChar** names); - static const UChar** loadData(UResourceBundle* rb, const char* key); + ~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) { -} - -ZNames::~ZNames() { - if (fNames != NULL) { - uprv_free(fNames); - } -} - -ZNames* -ZNames::createInstance(UResourceBundle* rb, const char* key) { - const UChar** names = loadData(rb, key); - if (names == NULL) { - // No names data available - return NULL; - } - return new ZNames(names); -} - -const UChar* -ZNames::getName(UTimeZoneNameType type) { - if (fNames == NULL) { - return NULL; - } - const UChar *name = NULL; - switch(type) { - case UTZNM_LONG_GENERIC: - name = fNames[0]; - break; - case UTZNM_LONG_STANDARD: - name = fNames[1]; - break; - case UTZNM_LONG_DAYLIGHT: - name = fNames[2]; - break; - case UTZNM_SHORT_GENERIC: - name = fNames[3]; - break; - case UTZNM_SHORT_STANDARD: - name = fNames[4]; - break; - case UTZNM_SHORT_DAYLIGHT: - name = fNames[5]; - break; - case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass - default: - name = NULL; - } - return name; -} - -const UChar** -ZNames::loadData(UResourceBundle* rb, const char* key) { - if (rb == NULL || key == NULL || *key == 0) { - return NULL; + // 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; } - UErrorCode status = U_ZERO_ERROR; - const UChar **names = NULL; + static void* createTimeZoneAndPutInCache(UHashtable* cache, const UChar* names[], + const UnicodeString& tzID, UErrorCode& status) { + if (U_FAILURE(status)) { return NULL; } + U_ASSERT(names != NULL); - UResourceBundle* rbTable = NULL; - rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status); - if (U_SUCCESS(status)) { - names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE); - if (names != NULL) { - UBool isEmpty = TRUE; - for (int32_t i = 0; i < KEYS_SIZE; i++) { - status = U_ZERO_ERROR; - int32_t len = 0; - const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status); - if (U_FAILURE(status) || len == 0) { - names[i] = NULL; - } else { - names[i] = value; - isEmpty = FALSE; + // If necessary, compute the location name from the time zone name. + UChar* locationName = NULL; + if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == NULL) { + UnicodeString locationNameUniStr; + TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr); + + // Copy the computed location name to the heap + if (locationNameUniStr.length() > 0) { + const UChar* buff = locationNameUniStr.getTerminatedBuffer(); + int32_t len = sizeof(UChar) * (locationNameUniStr.length() + 1); + locationName = (UChar*) uprv_malloc(len); + if (locationName == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + uprv_memcpy(locationName, buff, len); + } + } + + // Use the persistent ID as the resource key, so we can + // avoid duplications. + // TODO: Is there a more efficient way, like intern() in Java? + void* key = (void*) ZoneMeta::findTimeZoneID(tzID); + void* value = (void*) (new ZNames(names, locationName)); + if (value == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + uhash_put(cache, key, value, &status); + return value; + } + + const UChar* getName(UTimeZoneNameType type) const { + UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type); + return index >= 0 ? fNames[index] : NULL; + } + + void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) { + addNamesIntoTrie(mzID, NULL, trie, status); + } + void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) { + addNamesIntoTrie(NULL, tzID, trie, status); + } + + void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie, + UErrorCode& status) { + if (U_FAILURE(status)) { return; } + if (fDidAddIntoTrie) { return; } + fDidAddIntoTrie = TRUE; + + for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) { + const UChar* name = fNames[i]; + if (name != NULL) { + ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo)); + if (nameinfo == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + nameinfo->mzID = mzID; + nameinfo->tzID = tzID; + nameinfo->type = getTZNameType((UTimeZoneNameTypeIndex)i); + trie.put(name, nameinfo, status); // trie.put() takes ownership of the key + if (U_FAILURE(status)) { + return; } } - 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; - } - return ZNames::getName(type); -} - -TZNames* -TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) { - if (rb == NULL || key == NULL || *key == 0) { - return NULL; + /** Reset for loading another set of names. */ + void clear() { + uprv_memcpy(names, EMPTY_NAMES, sizeof(names)); } - const UChar** names = loadData(rb, key); - const UChar* locationName = NULL; - UChar* locationNameOwned = NULL; + void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) { + if (U_FAILURE(errorCode)) { return; } - UErrorCode status = U_ZERO_ERROR; - int32_t len = 0; + char key[ZID_KEY_MAX + 1]; + mergeTimeZoneKey(mzID, key); - UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status); - locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status); - // ignore missing resource here - status = U_ZERO_ERROR; + loadNames(zoneStrings, key, errorCode); + } - ures_close(table); + void 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); + } + } - if (locationName == NULL) { - UnicodeString tmpName; - int32_t tmpNameLen = 0; - TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName); - tmpNameLen = tmpName.length(); + char key[ZID_KEY_MAX + 1]; + uKey.extract(0, uKey.length(), key, sizeof(key), US_INV); - if (tmpNameLen > 0) { - locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1)); - if (locationNameOwned) { - tmpName.extract(locationNameOwned, tmpNameLen + 1, status); - locationName = locationNameOwned; + loadNames(zoneStrings, key, errorCode); + } + + void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) { + U_ASSERT(zoneStrings != NULL); + U_ASSERT(key != NULL); + U_ASSERT(key[0] != '\0'); + + UErrorCode localStatus = U_ZERO_ERROR; + clear(); + ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus); + + // Ignore errors, but propogate possible warnings. + if (U_SUCCESS(localStatus)) { + errorCode = localStatus; + } + } + + void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) { + UTimeZoneNameTypeIndex type = nameTypeFromKey(key); + if (type == UTZNM_INDEX_UNKNOWN) { return; } + if (names[type] == NULL) { + int32_t length; + // 'NO_NAME' indicates internally that this field should remain empty. It will be + // replaced by 'NULL' in getNames() + names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode); + } + } + + virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/, + UErrorCode &errorCode) { + ResourceTable namesTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) { + if (value.isNoInheritanceMarker()) { + setNameIfEmpty(key, NULL, errorCode); + } else { + setNameIfEmpty(key, &value, errorCode); } } } - TZNames* tznames = NULL; - if (locationName != NULL || names != NULL) { - tznames = new TZNames(names); - if (tznames == NULL) { - if (locationNameOwned) { - uprv_free(locationNameOwned); - } + static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) { + char c0, c1; + if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) { + return UTZNM_INDEX_UNKNOWN; } - tznames->fLocationName = locationName; - tznames->fLocationNameOwned = locationNameOwned; + 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; } - return tznames; -} + /** + * Returns an array of names. It is the caller's responsibility to copy the data into a + * permanent location, as the returned array is owned by the loader instance and may be + * cleared or leave scope. + * + * This is different than Java, where the array will no longer be modified and null + * may be returned. + */ + const UChar** getNames() { + // Remove 'NO_NAME' references in the array and replace with 'NULL' + for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) { + if (names[i] == NO_NAME) { + names[i] = NULL; + } + } + return names; + } +}; + +ZNames::ZNamesLoader::~ZNamesLoader() {} + // --------------------------------------------------- // The meta zone ID enumeration class @@ -774,25 +901,6 @@ MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() { } } -U_CDECL_BEGIN -/** - * ZNameInfo stores zone name information in the trie - */ -typedef struct ZNameInfo { - UTimeZoneNameType type; - const UChar* tzID; - const UChar* mzID; -} ZNameInfo; - -/** - * ZMatchInfo stores zone name match information used by find method - */ -typedef struct ZMatchInfo { - const ZNameInfo* znameInfo; - int32_t matchLength; -} ZMatchInfo; -U_CDECL_END - // --------------------------------------------------- // ZNameSearchHandler @@ -885,16 +993,7 @@ U_CDECL_BEGIN static void U_CALLCONV deleteZNames(void *obj) { if (obj != EMPTY) { - delete (ZNames *)obj; - } -} -/** - * Deleter for TZNames - */ -static void U_CALLCONV -deleteTZNames(void *obj) { - if (obj != EMPTY) { - delete (TZNames *)obj; + delete (ZNames*) obj; } } @@ -908,14 +1007,13 @@ deleteZNameInfo(void *obj) { U_CDECL_END -static UMutex gLock = U_MUTEX_INITIALIZER; - TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status) : fLocale(locale), fZoneStrings(NULL), fTZNamesMap(NULL), fMZNamesMap(NULL), fNamesTrieFullyLoaded(FALSE), + fNamesFullyLoaded(FALSE), fNamesTrie(TRUE, deleteZNameInfo) { initialize(locale, status); } @@ -945,7 +1043,7 @@ 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 @@ -1111,11 +1209,10 @@ TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID, ZNames *znames = NULL; TimeZoneNamesImpl *nonConstThis = const_cast(this); - umtx_lock(&gLock); { + Mutex lock(&gDataMutex); znames = nonConstThis->loadMetaZoneNames(mzID); } - umtx_unlock(&gLock); if (znames != NULL) { const UChar* s = znames->getName(type); @@ -1133,14 +1230,13 @@ TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNa return name; } - TZNames *tznames = NULL; + ZNames *tznames = NULL; TimeZoneNamesImpl *nonConstThis = const_cast(this); - umtx_lock(&gLock); { + Mutex lock(&gDataMutex); tznames = nonConstThis->loadTimeZoneNames(tzID); } - umtx_unlock(&gLock); if (tznames != NULL) { const UChar *s = tznames->getName(type); @@ -1155,14 +1251,13 @@ 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(this); - umtx_lock(&gLock); { + Mutex lock(&gDataMutex); tznames = nonConstThis->loadTimeZoneNames(tzID); } - umtx_unlock(&gLock); if (tznames != NULL) { locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION); @@ -1196,214 +1291,338 @@ static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) { */ ZNames* TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) { - if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) { - return NULL; - } - - ZNames *znames = 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 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* +ZNames* TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) { - if (tzID.length() > ZID_KEY_MAX) { - return NULL; - } - - TZNames *tznames = 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 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(this); - TimeZoneNamesImpl *nonConstThis = const_cast(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; - } - // loadStrings also load related metazone strings - nonConstThis->loadStrings(*id); - } - } - if (tzIDs != NULL) { - delete tzIDs; - } - if (U_SUCCESS(status)) { - nonConstThis->fNamesTrieFullyLoaded = TRUE; + pos = UHASH_FIRST; + while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) { + if (element->value.pointer == EMPTY) { continue; } + UChar* mzID = (UChar*) element->key.pointer; + ZNames* znames = (ZNames*) element->value.pointer; + znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status); + if (U_FAILURE(status)) { return; } + } + + pos = UHASH_FIRST; + while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) { + if (element->value.pointer == EMPTY) { continue; } + UChar* tzID = (UChar*) element->key.pointer; + ZNames* znames = (ZNames*) element->value.pointer; + znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status); + if (U_FAILURE(status)) { return; } + } +} + +U_CDECL_BEGIN +static void U_CALLCONV +deleteZNamesLoader(void* obj) { + if (obj == DUMMY_LOADER) { return; } + const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj; + delete loader; +} +U_CDECL_END + +struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink { + TimeZoneNamesImpl& tzn; + UHashtable* keyToLoader; + + ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status) + : tzn(_tzn) { + keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); + if (U_FAILURE(status)) { return; } + uhash_setKeyDeleter(keyToLoader, uprv_free); + uhash_setValueDeleter(keyToLoader, deleteZNamesLoader); + } + virtual ~ZoneStringsLoader(); + + void* createKey(const char* key, UErrorCode& status) { + int32_t len = sizeof(char) * (uprv_strlen(key) + 1); + char* newKey = (char*) uprv_malloc(len); + if (newKey == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + uprv_memcpy(newKey, key, len); + newKey[len-1] = '\0'; + return (void*) newKey; + } + + UBool isMetaZone(const char* key) { + return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0); + } + + UnicodeString mzIDFromKey(const char* key) { + return UnicodeString(key + MZ_PREFIX_LEN, uprv_strlen(key) - MZ_PREFIX_LEN, US_INV); + } + + UnicodeString tzIDFromKey(const char* key) { + UnicodeString tzID(key, -1, US_INV); + // Replace all colons ':' with slashes '/' + for (int i=0; ivalue.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) { + + void* loader = uhash_get(keyToLoader, key); + if (loader == NULL) { + if (isMetaZone(key)) { + UnicodeString mzID = mzIDFromKey(key); + void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer()); + if (cacheVal != NULL) { + // We have already loaded the names for this meta zone. + loader = (void*) DUMMY_LOADER; + } else { + loader = (void*) new ZNames::ZNamesLoader(); + if (loader == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + } else { + UnicodeString tzID = tzIDFromKey(key); + void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer()); + if (cacheVal != NULL) { + // We have already loaded the names for this time zone. + loader = (void*) DUMMY_LOADER; + } else { + loader = (void*) new ZNames::ZNamesLoader(); + if (loader == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + } + + void* newKey = createKey(key, status); + if (U_FAILURE(status)) { return; } + uhash_put(keyToLoader, newKey, loader, &status); + if (U_FAILURE(status)) { return; } + } + + if (loader != DUMMY_LOADER) { + // Let the ZNamesLoader consume the names table. + ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status); + } + } + + void consumeNoFallback(const char* key, UErrorCode& status) { + void* loader = uhash_get(keyToLoader, key); + if (loader == NULL) { + void* newKey = createKey(key, status); + if (U_FAILURE(status)) { return; } + uhash_put(keyToLoader, newKey, (void*) DUMMY_LOADER, &status); + if (U_FAILURE(status)) { return; } + } + } + + 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) { + if (value.isNoInheritanceMarker()) { + consumeNoFallback(key, status); + } else if (value.getType() == URES_TABLE) { + consumeNamesTable(key, value, noFallback, status); + } else { + // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard). + // All time zone fields are tables. + } + if (U_FAILURE(status)) { return; } + } + } +}; + +// Virtual destructors must be defined out of line. +TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() { + uhash_close(keyToLoader); } +void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) { + if (U_FAILURE(status)) return; + + { + Mutex lock(&gDataMutex); + internalLoadAllDisplayNames(status); + } +} + +// Caller must synchronize. +void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) { + if (!fNamesFullyLoaded) { + fNamesFullyLoaded = TRUE; + + ZoneStringsLoader loader(*this, status); + loader.load(status); + if (U_FAILURE(status)) { return; } + + const UnicodeString *id; + + // load strings for all zones + StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration( + UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); + if (U_SUCCESS(status)) { + while ((id = tzIDs->snext(status))) { + if (U_FAILURE(status)) { + break; + } + UnicodeString copy(*id); + void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer()); + if (value == NULL) { + // loadStrings also load related metazone strings + loadStrings(*id); + } + } + } + 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/ @@ -1984,9 +2203,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; diff --git a/icu4c/source/i18n/tznames_impl.h b/icu4c/source/i18n/tznames_impl.h index 616756c8f4..6ad61c3c66 100644 --- a/icu4c/source/i18n/tznames_impl.h +++ b/icu4c/source/i18n/tznames_impl.h @@ -2,8 +2,8 @@ // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* - * Copyright (C) 2011-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * + * Copyright (C) 2011-2016, International Business Machines Corporation and + * others. All Rights Reserved. ******************************************************************************* */ @@ -161,8 +161,8 @@ private: class ZNames; -class TZNames; class TextTrieMap; +class ZNameSearchHandler; class TimeZoneNamesImpl : public TimeZoneNames { public: @@ -186,6 +186,9 @@ public: TimeZoneNames::MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; + void loadAllDisplayNames(UErrorCode& errorCode); + void internalLoadAllDisplayNames(UErrorCode& errorCode); + static UnicodeString& getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name); static StringEnumeration* _getAvailableMetaZoneIDs(UErrorCode& status); @@ -203,6 +206,7 @@ private: UHashtable* fMZNamesMap; UBool fNamesTrieFullyLoaded; + UBool fNamesFullyLoaded; TextTrieMap fNamesTrie; void initialize(const Locale& locale, UErrorCode& status); @@ -211,7 +215,12 @@ private: void loadStrings(const UnicodeString& tzCanonicalID); ZNames* loadMetaZoneNames(const UnicodeString& mzId); - TZNames* loadTimeZoneNames(const UnicodeString& mzId); + ZNames* loadTimeZoneNames(const UnicodeString& mzId); + TimeZoneNames::MatchInfoCollection* doFind(ZNameSearchHandler& handler, + const UnicodeString& text, int32_t start, UErrorCode& status) const; + void addAllNamesIntoTrie(UErrorCode& errorCode); + + struct ZoneStringsLoader; }; class TZDBNames; diff --git a/icu4c/source/i18n/unicode/tznames.h b/icu4c/source/i18n/unicode/tznames.h index cbcf9bbb2d..c0dadf70f3 100644 --- a/icu4c/source/i18n/unicode/tznames.h +++ b/icu4c/source/i18n/unicode/tznames.h @@ -2,7 +2,7 @@ // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* -* Copyright (C) 2011-2015, International Business Machines Corporation and +* Copyright (C) 2011-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ @@ -135,7 +135,7 @@ public: virtual ~TimeZoneNames(); /** - * Return true if the given TimeZoneNames objects are emantically equal. + * Return true if the given TimeZoneNames objects are semantically equal. * @param other the object to be compared with. * @return Return TRUE if the given Format objects are semantically equal. * @stable ICU 50 @@ -290,6 +290,18 @@ public: */ virtual UnicodeString& getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const; + /** + * @internal For specific users only until proposed publicly. + * @deprecated This API is ICU internal only. + */ + virtual void loadAllDisplayNames(UErrorCode& status); + + /** + * @internal For specific users only until proposed publicly. + * @deprecated This API is ICU internal only. + */ + virtual void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[]) const; + /** * MatchInfoCollection represents a collection of time zone name matches used by * {@link TimeZoneNames#find}. diff --git a/icu4c/source/test/intltest/tzfmttst.cpp b/icu4c/source/test/intltest/tzfmttst.cpp index a505d3ee9f..b5b9080872 100644 --- a/icu4c/source/test/intltest/tzfmttst.cpp +++ b/icu4c/source/test/intltest/tzfmttst.cpp @@ -833,7 +833,7 @@ TimeZoneFormatTest::TestParse(void) { delete tz; } else { if (DATA[i].expected) { - errln((UnicodeString)"Fail: Parse failure - expected: " + DATA[i].expected); + errMsg = (UnicodeString)"Parse failure - expected: " + DATA[i].expected; } } if (errMsg.length() > 0) {