summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/tznames.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/tznames.cpp')
-rw-r--r--intl/icu/source/i18n/tznames.cpp507
1 files changed, 507 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/tznames.cpp b/intl/icu/source/i18n/tznames.cpp
new file mode 100644
index 0000000000..900499fd40
--- /dev/null
+++ b/intl/icu/source/i18n/tznames.cpp
@@ -0,0 +1,507 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+* Copyright (C) 2011-2015, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/locid.h"
+#include "unicode/tznames.h"
+#include "unicode/uenum.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "mutex.h"
+#include "putilimp.h"
+#include "tznames_impl.h"
+#include "uassert.h"
+#include "ucln_in.h"
+#include "uhash.h"
+#include "umutex.h"
+#include "uvector.h"
+
+
+U_NAMESPACE_BEGIN
+
+// TimeZoneNames object cache handling
+static UMutex gTimeZoneNamesLock;
+static UHashtable *gTimeZoneNamesCache = nullptr;
+static UBool gTimeZoneNamesCacheInitialized = false;
+
+// Access count - incremented every time up to SWEEP_INTERVAL,
+// then reset to 0
+static int32_t gAccessCount = 0;
+
+// Interval for calling the cache sweep function - every 100 times
+#define SWEEP_INTERVAL 100
+
+// Cache expiration in millisecond. When a cached entry is no
+// longer referenced and exceeding this threshold since last
+// access time, then the cache entry will be deleted by the sweep
+// function. For now, 3 minutes.
+#define CACHE_EXPIRATION 180000.0
+
+typedef struct TimeZoneNamesCacheEntry {
+ TimeZoneNames* names;
+ int32_t refCount;
+ double lastAccess;
+} TimeZoneNamesCacheEntry;
+
+U_CDECL_BEGIN
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV timeZoneNames_cleanup()
+{
+ if (gTimeZoneNamesCache != nullptr) {
+ uhash_close(gTimeZoneNamesCache);
+ gTimeZoneNamesCache = nullptr;
+ }
+ gTimeZoneNamesCacheInitialized = false;
+ return true;
+}
+
+/**
+ * Deleter for TimeZoneNamesCacheEntry
+ */
+static void U_CALLCONV
+deleteTimeZoneNamesCacheEntry(void *obj) {
+ icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
+ delete (icu::TimeZoneNamesImpl*) entry->names;
+ uprv_free(entry);
+}
+U_CDECL_END
+
+/**
+ * Function used for removing unreferrenced cache entries exceeding
+ * the expiration time. This function must be called with in the mutex
+ * block.
+ */
+static void sweepCache() {
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* elem;
+ double now = (double)uprv_getUTCtime();
+
+ while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos)) != 0) {
+ TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer;
+ if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
+ // delete this entry
+ uhash_removeElement(gTimeZoneNamesCache, elem);
+ }
+ }
+}
+
+// ---------------------------------------------------
+// TimeZoneNamesDelegate
+// ---------------------------------------------------
+class TimeZoneNamesDelegate : public TimeZoneNames {
+public:
+ TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
+ virtual ~TimeZoneNamesDelegate();
+
+ virtual bool operator==(const TimeZoneNames& other) const override;
+ virtual bool operator!=(const TimeZoneNames& other) const {return !operator==(other);}
+ virtual TimeZoneNamesDelegate* clone() const override;
+
+ StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const override;
+ StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const override;
+ UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const override;
+ UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const override;
+
+ UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const override;
+ UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const override;
+
+ UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const override;
+
+ void loadAllDisplayNames(UErrorCode& status) override;
+ void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const override;
+
+ MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const override;
+private:
+ TimeZoneNamesDelegate();
+ TimeZoneNamesCacheEntry* fTZnamesCacheEntry;
+};
+
+TimeZoneNamesDelegate::TimeZoneNamesDelegate()
+: fTZnamesCacheEntry(0) {
+}
+
+TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
+ Mutex lock(&gTimeZoneNamesLock);
+ if (!gTimeZoneNamesCacheInitialized) {
+ // Create empty hashtable if it is not already initialized.
+ gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
+ if (U_SUCCESS(status)) {
+ uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free);
+ uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry);
+ gTimeZoneNamesCacheInitialized = true;
+ ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup);
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ // Check the cache, if not available, create new one and cache
+ TimeZoneNamesCacheEntry *cacheEntry = nullptr;
+
+ const char *key = locale.getName();
+ cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key);
+ if (cacheEntry == nullptr) {
+ TimeZoneNames *tznames = nullptr;
+ char *newKey = nullptr;
+
+ tznames = new TimeZoneNamesImpl(locale, status);
+ if (tznames == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ if (U_SUCCESS(status)) {
+ newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
+ if (newKey == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ uprv_strcpy(newKey, key);
+ }
+ }
+ if (U_SUCCESS(status)) {
+ cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry));
+ if (cacheEntry == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ cacheEntry->names = tznames;
+ cacheEntry->refCount = 1;
+ cacheEntry->lastAccess = (double)uprv_getUTCtime();
+
+ uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
+ }
+ }
+ if (U_FAILURE(status)) {
+ if (tznames != nullptr) {
+ delete tznames;
+ }
+ if (newKey != nullptr) {
+ uprv_free(newKey);
+ }
+ if (cacheEntry != nullptr) {
+ uprv_free(cacheEntry);
+ }
+ cacheEntry = nullptr;
+ }
+ } else {
+ // Update the reference count
+ cacheEntry->refCount++;
+ cacheEntry->lastAccess = (double)uprv_getUTCtime();
+ }
+ gAccessCount++;
+ if (gAccessCount >= SWEEP_INTERVAL) {
+ // sweep
+ sweepCache();
+ gAccessCount = 0;
+ }
+ fTZnamesCacheEntry = cacheEntry;
+}
+
+TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
+ umtx_lock(&gTimeZoneNamesLock);
+ {
+ if (fTZnamesCacheEntry) {
+ U_ASSERT(fTZnamesCacheEntry->refCount > 0);
+ // Just decrement the reference count
+ fTZnamesCacheEntry->refCount--;
+ }
+ }
+ umtx_unlock(&gTimeZoneNamesLock);
+}
+
+bool
+TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
+ if (this == &other) {
+ return true;
+ }
+ // Just compare if the other object also use the same
+ // cache entry
+ const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
+ if (rhs) {
+ return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
+ }
+ return false;
+}
+
+TimeZoneNamesDelegate*
+TimeZoneNamesDelegate::clone() const {
+ TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
+ if (other != nullptr) {
+ umtx_lock(&gTimeZoneNamesLock);
+ {
+ // Just increment the reference count
+ fTZnamesCacheEntry->refCount++;
+ other->fTZnamesCacheEntry = fTZnamesCacheEntry;
+ }
+ umtx_unlock(&gTimeZoneNamesLock);
+ }
+ return other;
+}
+
+StringEnumeration*
+TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
+ return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
+}
+
+StringEnumeration*
+TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
+ return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
+}
+
+UnicodeString&
+TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
+ return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
+}
+
+UnicodeString&
+TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
+ return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
+}
+
+UnicodeString&
+TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
+ return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
+}
+
+UnicodeString&
+TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
+ return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
+}
+
+UnicodeString&
+TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
+ return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
+}
+
+void
+TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) {
+ fTZnamesCacheEntry->names->loadAllDisplayNames(status);
+}
+
+void
+TimeZoneNamesDelegate::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
+ fTZnamesCacheEntry->names->getDisplayNames(tzID, types, numTypes, date, dest, 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);
+}
+
+// ---------------------------------------------------
+// TimeZoneNames base class
+// ---------------------------------------------------
+TimeZoneNames::~TimeZoneNames() {
+}
+
+TimeZoneNames*
+TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
+ TimeZoneNames *instance = nullptr;
+ if (U_SUCCESS(status)) {
+ instance = new TimeZoneNamesDelegate(locale, status);
+ if (instance == nullptr && U_SUCCESS(status)) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ return instance;
+}
+
+TimeZoneNames*
+TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) {
+ TimeZoneNames *instance = nullptr;
+ if (U_SUCCESS(status)) {
+ instance = new TZDBTimeZoneNames(locale);
+ if (instance == nullptr && U_SUCCESS(status)) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ return instance;
+}
+
+UnicodeString&
+TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
+ return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
+}
+
+UnicodeString&
+TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
+ getTimeZoneDisplayName(tzID, type, name);
+ if (name.isEmpty()) {
+ char16_t mzIDBuf[32];
+ UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
+ getMetaZoneID(tzID, date, mzID);
+ getMetaZoneDisplayName(mzID, type, name);
+ }
+ return name;
+}
+
+// Empty default implementation, to be overridden in tznames_impl.cpp.
+void
+TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) {
+}
+
+// A default, lightweight implementation of getDisplayNames.
+// Overridden in tznames_impl.cpp.
+void
+TimeZoneNames::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; }
+ UnicodeString mzID;
+ for (int i = 0; i < numTypes; i++) {
+ getTimeZoneDisplayName(tzID, types[i], dest[i]);
+ if (dest[i].isEmpty()) {
+ if (mzID.isEmpty()) {
+ getMetaZoneID(tzID, date, mzID);
+ }
+ getMetaZoneDisplayName(mzID, types[i], dest[i]);
+ }
+ }
+}
+
+
+struct MatchInfo : UMemory {
+ UTimeZoneNameType nameType;
+ UnicodeString id;
+ int32_t matchLength;
+ UBool isTZID;
+
+ MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
+ this->nameType = nameType;
+ this->matchLength = matchLength;
+ if (tzID != nullptr) {
+ this->id.setTo(*tzID);
+ this->isTZID = true;
+ } else {
+ this->id.setTo(*mzID);
+ this->isTZID = false;
+ }
+ }
+};
+
+U_CDECL_BEGIN
+static void U_CALLCONV
+deleteMatchInfo(void *obj) {
+ delete static_cast<MatchInfo *>(obj);
+}
+U_CDECL_END
+
+// ---------------------------------------------------
+// MatchInfoCollection class
+// ---------------------------------------------------
+TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
+: fMatches(nullptr) {
+}
+
+TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
+ if (fMatches != nullptr) {
+ delete fMatches;
+ }
+}
+
+void
+TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
+ const UnicodeString& tzID, UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ LocalPointer <MatchInfo> matchInfo(new MatchInfo(nameType, matchLength, &tzID, nullptr), status);
+ UVector *matchesVec = matches(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ matchesVec->adoptElement(matchInfo.orphan(), status);
+}
+
+void
+TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
+ const UnicodeString& mzID, UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ LocalPointer<MatchInfo> matchInfo(new MatchInfo(nameType, matchLength, nullptr, &mzID), status);
+ UVector *matchesVec = matches(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ matchesVec->adoptElement(matchInfo.orphan(), status);
+}
+
+int32_t
+TimeZoneNames::MatchInfoCollection::size() const {
+ if (fMatches == nullptr) {
+ return 0;
+ }
+ return fMatches->size();
+}
+
+UTimeZoneNameType
+TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
+ const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
+ if (match) {
+ return match->nameType;
+ }
+ return UTZNM_UNKNOWN;
+}
+
+int32_t
+TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
+ const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
+ if (match) {
+ return match->matchLength;
+ }
+ return 0;
+}
+
+UBool
+TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
+ tzID.remove();
+ const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
+ if (match && match->isTZID) {
+ tzID.setTo(match->id);
+ return true;
+ }
+ return false;
+}
+
+UBool
+TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
+ mzID.remove();
+ const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
+ if (match && !match->isTZID) {
+ mzID.setTo(match->id);
+ return true;
+ }
+ return false;
+}
+
+UVector*
+TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ if (fMatches != nullptr) {
+ return fMatches;
+ }
+ fMatches = new UVector(deleteMatchInfo, nullptr, status);
+ if (fMatches == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else if (U_FAILURE(status)) {
+ delete fMatches;
+ fMatches = nullptr;
+ }
+ return fMatches;
+}
+
+
+U_NAMESPACE_END
+#endif