LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - tzgnames.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 595 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 41 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // © 2016 and later: Unicode, Inc. and others.
       2             : // License & terms of use: http://www.unicode.org/copyright.html
       3             : /*
       4             : *******************************************************************************
       5             : * Copyright (C) 2011-2016, International Business Machines Corporation and
       6             : * others. All Rights Reserved.
       7             : *******************************************************************************
       8             : */
       9             : 
      10             : #include "unicode/utypes.h"
      11             : 
      12             : #if !UCONFIG_NO_FORMATTING
      13             : 
      14             : #include "tzgnames.h"
      15             : 
      16             : #include "unicode/basictz.h"
      17             : #include "unicode/locdspnm.h"
      18             : #include "unicode/rbtz.h"
      19             : #include "unicode/simpleformatter.h"
      20             : #include "unicode/simpletz.h"
      21             : #include "unicode/strenum.h"
      22             : #include "unicode/vtzone.h"
      23             : 
      24             : #include "cmemory.h"
      25             : #include "cstring.h"
      26             : #include "mutex.h"
      27             : #include "uhash.h"
      28             : #include "uassert.h"
      29             : #include "umutex.h"
      30             : #include "uresimp.h"
      31             : #include "ureslocs.h"
      32             : #include "zonemeta.h"
      33             : #include "tznames_impl.h"
      34             : #include "olsontz.h"
      35             : #include "ucln_in.h"
      36             : 
      37             : U_NAMESPACE_BEGIN
      38             : 
      39             : #define ZID_KEY_MAX  128
      40             : 
      41             : static const char gZoneStrings[]                = "zoneStrings";
      42             : 
      43             : static const char gRegionFormatTag[]            = "regionFormat";
      44             : static const char gFallbackFormatTag[]          = "fallbackFormat";
      45             : 
      46             : static const UChar gEmpty[]                     = {0x00};
      47             : 
      48             : static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
      49             : static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
      50             : 
      51             : static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
      52             : 
      53             : 
      54             : 
      55             : U_CDECL_BEGIN
      56             : 
      57             : typedef struct PartialLocationKey {
      58             :     const UChar* tzID;
      59             :     const UChar* mzID;
      60             :     UBool isLong;
      61             : } PartialLocationKey;
      62             : 
      63             : /**
      64             :  * Hash function for partial location name hash key
      65             :  */
      66             : static int32_t U_CALLCONV
      67           0 : hashPartialLocationKey(const UHashTok key) {
      68             :     // <tzID>&<mzID>#[L|S]
      69           0 :     PartialLocationKey *p = (PartialLocationKey *)key.pointer;
      70           0 :     UnicodeString str(p->tzID);
      71           0 :     str.append((UChar)0x26)
      72           0 :         .append(p->mzID, -1)
      73           0 :         .append((UChar)0x23)
      74           0 :         .append((UChar)(p->isLong ? 0x4C : 0x53));
      75           0 :     return str.hashCode();
      76             : }
      77             : 
      78             : /**
      79             :  * Comparer for partial location name hash key
      80             :  */
      81             : static UBool U_CALLCONV
      82           0 : comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
      83           0 :     PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
      84           0 :     PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
      85             : 
      86           0 :     if (p1 == p2) {
      87           0 :         return TRUE;
      88             :     }
      89           0 :     if (p1 == NULL || p2 == NULL) {
      90           0 :         return FALSE;
      91             :     }
      92             :     // We just check identity of tzID/mzID
      93           0 :     return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
      94             : }
      95             : 
      96             : /**
      97             :  * Deleter for GNameInfo
      98             :  */
      99             : static void U_CALLCONV
     100           0 : deleteGNameInfo(void *obj) {
     101           0 :     uprv_free(obj);
     102           0 : }
     103             : 
     104             : /**
     105             :  * GNameInfo stores zone name information in the local trie
     106             :  */
     107             : typedef struct GNameInfo {
     108             :     UTimeZoneGenericNameType    type;
     109             :     const UChar*                tzID;
     110             : } ZNameInfo;
     111             : 
     112             : /**
     113             :  * GMatchInfo stores zone name match information used by find method
     114             :  */
     115             : typedef struct GMatchInfo {
     116             :     const GNameInfo*    gnameInfo;
     117             :     int32_t             matchLength;
     118             :     UTimeZoneFormatTimeType   timeType;
     119             : } ZMatchInfo;
     120             : 
     121             : U_CDECL_END
     122             : 
     123             : // ---------------------------------------------------
     124             : // The class stores time zone generic name match information
     125             : // ---------------------------------------------------
     126             : class TimeZoneGenericNameMatchInfo : public UMemory {
     127             : public:
     128             :     TimeZoneGenericNameMatchInfo(UVector* matches);
     129             :     ~TimeZoneGenericNameMatchInfo();
     130             : 
     131             :     int32_t size() const;
     132             :     UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
     133             :     int32_t getMatchLength(int32_t index) const;
     134             :     UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
     135             : 
     136             : private:
     137             :     UVector* fMatches;  // vector of MatchEntry
     138             : };
     139             : 
     140           0 : TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
     141           0 : : fMatches(matches) {
     142           0 : }
     143             : 
     144           0 : TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
     145           0 :     if (fMatches != NULL) {
     146           0 :         delete fMatches;
     147             :     }
     148           0 : }
     149             : 
     150             : int32_t
     151           0 : TimeZoneGenericNameMatchInfo::size() const {
     152           0 :     if (fMatches == NULL) {
     153           0 :         return 0;
     154             :     }
     155           0 :     return fMatches->size();
     156             : }
     157             : 
     158             : UTimeZoneGenericNameType
     159           0 : TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
     160           0 :     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
     161           0 :     if (minfo != NULL) {
     162           0 :         return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
     163             :     }
     164           0 :     return UTZGNM_UNKNOWN;
     165             : }
     166             : 
     167             : int32_t
     168           0 : TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
     169           0 :     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
     170           0 :     if (minfo != NULL) {
     171           0 :         return minfo->matchLength;
     172             :     }
     173           0 :     return -1;
     174             : }
     175             : 
     176             : UnicodeString&
     177           0 : TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
     178           0 :     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
     179           0 :     if (minfo != NULL && minfo->gnameInfo->tzID != NULL) {
     180           0 :         tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1);
     181             :     } else {
     182           0 :         tzID.setToBogus();
     183             :     }
     184           0 :     return tzID;
     185             : }
     186             : 
     187             : // ---------------------------------------------------
     188             : // GNameSearchHandler
     189             : // ---------------------------------------------------
     190             : class GNameSearchHandler : public TextTrieMapSearchResultHandler {
     191             : public:
     192             :     GNameSearchHandler(uint32_t types);
     193             :     virtual ~GNameSearchHandler();
     194             : 
     195             :     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
     196             :     UVector* getMatches(int32_t& maxMatchLen);
     197             : 
     198             : private:
     199             :     uint32_t fTypes;
     200             :     UVector* fResults;
     201             :     int32_t fMaxMatchLen;
     202             : };
     203             : 
     204           0 : GNameSearchHandler::GNameSearchHandler(uint32_t types)
     205           0 : : fTypes(types), fResults(NULL), fMaxMatchLen(0) {
     206           0 : }
     207             : 
     208           0 : GNameSearchHandler::~GNameSearchHandler() {
     209           0 :     if (fResults != NULL) {
     210           0 :         delete fResults;
     211             :     }
     212           0 : }
     213             : 
     214             : UBool
     215           0 : GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
     216           0 :     if (U_FAILURE(status)) {
     217           0 :         return FALSE;
     218             :     }
     219           0 :     if (node->hasValues()) {
     220           0 :         int32_t valuesCount = node->countValues();
     221           0 :         for (int32_t i = 0; i < valuesCount; i++) {
     222           0 :             GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
     223           0 :             if (nameinfo == NULL) {
     224           0 :                 break;
     225             :             }
     226           0 :             if ((nameinfo->type & fTypes) != 0) {
     227             :                 // matches a requested type
     228           0 :                 if (fResults == NULL) {
     229           0 :                     fResults = new UVector(uprv_free, NULL, status);
     230           0 :                     if (fResults == NULL) {
     231           0 :                         status = U_MEMORY_ALLOCATION_ERROR;
     232             :                     }
     233             :                 }
     234           0 :                 if (U_SUCCESS(status)) {
     235           0 :                     U_ASSERT(fResults != NULL);
     236           0 :                     GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));
     237           0 :                     if (gmatch == NULL) {
     238           0 :                         status = U_MEMORY_ALLOCATION_ERROR;
     239             :                     } else {
     240             :                         // add the match to the vector
     241           0 :                         gmatch->gnameInfo = nameinfo;
     242           0 :                         gmatch->matchLength = matchLength;
     243           0 :                         gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
     244           0 :                         fResults->addElement(gmatch, status);
     245           0 :                         if (U_FAILURE(status)) {
     246           0 :                             uprv_free(gmatch);
     247             :                         } else {
     248           0 :                             if (matchLength > fMaxMatchLen) {
     249           0 :                                 fMaxMatchLen = matchLength;
     250             :                             }
     251             :                         }
     252             :                     }
     253             :                 }
     254             :             }
     255             :         }
     256             :     }
     257           0 :     return TRUE;
     258             : }
     259             : 
     260             : UVector*
     261           0 : GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
     262             :     // give the ownership to the caller
     263           0 :     UVector *results = fResults;
     264           0 :     maxMatchLen = fMaxMatchLen;
     265             : 
     266             :     // reset
     267           0 :     fResults = NULL;
     268           0 :     fMaxMatchLen = 0;
     269           0 :     return results;
     270             : }
     271             : 
     272             : static UMutex gLock = U_MUTEX_INITIALIZER;
     273             : 
     274             : class TZGNCore : public UMemory {
     275             : public:
     276             :     TZGNCore(const Locale& locale, UErrorCode& status);
     277             :     virtual ~TZGNCore();
     278             : 
     279             :     UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
     280             :                         UDate date, UnicodeString& name) const;
     281             : 
     282             :     UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
     283             : 
     284             :     int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
     285             :         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
     286             : 
     287             : private:
     288             :     Locale fLocale;
     289             :     const TimeZoneNames* fTimeZoneNames;
     290             :     UHashtable* fLocationNamesMap;
     291             :     UHashtable* fPartialLocationNamesMap;
     292             : 
     293             :     SimpleFormatter fRegionFormat;
     294             :     SimpleFormatter fFallbackFormat;
     295             : 
     296             :     LocaleDisplayNames* fLocaleDisplayNames;
     297             :     ZNStringPool fStringPool;
     298             : 
     299             :     TextTrieMap fGNamesTrie;
     300             :     UBool fGNamesTrieFullyLoaded;
     301             : 
     302             :     char fTargetRegion[ULOC_COUNTRY_CAPACITY];
     303             : 
     304             :     void initialize(const Locale& locale, UErrorCode& status);
     305             :     void cleanup();
     306             : 
     307             :     void loadStrings(const UnicodeString& tzCanonicalID);
     308             : 
     309             :     const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);
     310             : 
     311             :     UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
     312             :                         UDate date, UnicodeString& name) const;
     313             : 
     314             :     UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
     315             :                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
     316             :                         UnicodeString& name) const;
     317             : 
     318             :     const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,
     319             :                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
     320             : 
     321             :     TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
     322             : 
     323             :     TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
     324             : };
     325             : 
     326             : 
     327             : // ---------------------------------------------------
     328             : // TZGNCore - core implmentation of TimeZoneGenericNames
     329             : //
     330             : // TimeZoneGenericNames is parallel to TimeZoneNames,
     331             : // but handles run-time generated time zone names.
     332             : // This is the main part of this module.
     333             : // ---------------------------------------------------
     334           0 : TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
     335             : : fLocale(locale),
     336             :   fTimeZoneNames(NULL),
     337             :   fLocationNamesMap(NULL),
     338             :   fPartialLocationNamesMap(NULL),
     339             :   fLocaleDisplayNames(NULL),
     340             :   fStringPool(status),
     341             :   fGNamesTrie(TRUE, deleteGNameInfo),
     342           0 :   fGNamesTrieFullyLoaded(FALSE) {
     343           0 :     initialize(locale, status);
     344           0 : }
     345             : 
     346           0 : TZGNCore::~TZGNCore() {
     347           0 :     cleanup();
     348           0 : }
     349             : 
     350             : void
     351           0 : TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
     352           0 :     if (U_FAILURE(status)) {
     353           0 :         return;
     354             :     }
     355             : 
     356             :     // TimeZoneNames
     357           0 :     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
     358           0 :     if (U_FAILURE(status)) {
     359           0 :         return;
     360             :     }
     361             : 
     362             :     // Initialize format patterns
     363           0 :     UnicodeString rpat(TRUE, gDefRegionPattern, -1);
     364           0 :     UnicodeString fpat(TRUE, gDefFallbackPattern, -1);
     365             : 
     366           0 :     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
     367           0 :     UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
     368           0 :     zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
     369             : 
     370           0 :     if (U_SUCCESS(tmpsts)) {
     371           0 :         const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);
     372           0 :         if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
     373           0 :             rpat.setTo(regionPattern, -1);
     374             :         }
     375           0 :         tmpsts = U_ZERO_ERROR;
     376           0 :         const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts);
     377           0 :         if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
     378           0 :             fpat.setTo(fallbackPattern, -1);
     379             :         }
     380             :     }
     381           0 :     ures_close(zoneStrings);
     382             : 
     383           0 :     fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
     384           0 :     fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
     385           0 :     if (U_FAILURE(status)) {
     386           0 :         cleanup();
     387           0 :         return;
     388             :     }
     389             : 
     390             :     // locale display names
     391           0 :     fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
     392             : 
     393             :     // hash table for names - no key/value deleters
     394           0 :     fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
     395           0 :     if (U_FAILURE(status)) {
     396           0 :         cleanup();
     397           0 :         return;
     398             :     }
     399             : 
     400           0 :     fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status);
     401           0 :     if (U_FAILURE(status)) {
     402           0 :         cleanup();
     403           0 :         return;
     404             :     }
     405           0 :     uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
     406             :     // no value deleter
     407             : 
     408             :     // target region
     409           0 :     const char* region = fLocale.getCountry();
     410           0 :     int32_t regionLen = uprv_strlen(region);
     411           0 :     if (regionLen == 0) {
     412             :         char loc[ULOC_FULLNAME_CAPACITY];
     413           0 :         uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
     414             : 
     415           0 :         regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
     416           0 :         if (U_SUCCESS(status)) {
     417           0 :             fTargetRegion[regionLen] = 0;
     418             :         } else {
     419           0 :             cleanup();
     420           0 :             return;
     421             :         }
     422           0 :     } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
     423           0 :         uprv_strcpy(fTargetRegion, region);
     424             :     } else {
     425           0 :         fTargetRegion[0] = 0;
     426             :     }
     427             : 
     428             :     // preload generic names for the default zone
     429           0 :     TimeZone *tz = TimeZone::createDefault();
     430           0 :     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
     431           0 :     if (tzID != NULL) {
     432           0 :         loadStrings(UnicodeString(TRUE, tzID, -1));
     433             :     }
     434           0 :     delete tz;
     435             : }
     436             : 
     437             : void
     438           0 : TZGNCore::cleanup() {
     439           0 :     if (fLocaleDisplayNames != NULL) {
     440           0 :         delete fLocaleDisplayNames;
     441             :     }
     442           0 :     if (fTimeZoneNames != NULL) {
     443           0 :         delete fTimeZoneNames;
     444             :     }
     445             : 
     446           0 :     uhash_close(fLocationNamesMap);
     447           0 :     uhash_close(fPartialLocationNamesMap);
     448           0 : }
     449             : 
     450             : 
     451             : UnicodeString&
     452           0 : TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
     453           0 :     name.setToBogus();
     454           0 :     switch (type) {
     455             :     case UTZGNM_LOCATION:
     456             :         {
     457           0 :             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
     458           0 :             if (tzCanonicalID != NULL) {
     459           0 :                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
     460             :             }
     461             :         }
     462           0 :         break;
     463             :     case UTZGNM_LONG:
     464             :     case UTZGNM_SHORT:
     465           0 :         formatGenericNonLocationName(tz, type, date, name);
     466           0 :         if (name.isEmpty()) {
     467           0 :             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
     468           0 :             if (tzCanonicalID != NULL) {
     469           0 :                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
     470             :             }
     471             :         }
     472           0 :         break;
     473             :     default:
     474           0 :         break;
     475             :     }
     476           0 :     return name;
     477             : }
     478             : 
     479             : UnicodeString&
     480           0 : TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
     481           0 :     if (tzCanonicalID.isEmpty()) {
     482           0 :         name.setToBogus();
     483           0 :         return name;
     484             :     }
     485             : 
     486           0 :     const UChar *locname = NULL;
     487           0 :     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
     488           0 :     umtx_lock(&gLock);
     489             :     {
     490           0 :         locname = nonConstThis->getGenericLocationName(tzCanonicalID);
     491             :     }
     492           0 :     umtx_unlock(&gLock);
     493             : 
     494           0 :     if (locname == NULL) {
     495           0 :         name.setToBogus();
     496             :     } else {
     497           0 :         name.setTo(locname, u_strlen(locname));
     498             :     }
     499             : 
     500           0 :     return name;
     501             : }
     502             : 
     503             : /*
     504             :  * This method updates the cache and must be called with a lock
     505             :  */
     506             : const UChar*
     507           0 : TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
     508           0 :     U_ASSERT(!tzCanonicalID.isEmpty());
     509           0 :     if (tzCanonicalID.length() > ZID_KEY_MAX) {
     510           0 :         return NULL;
     511             :     }
     512             : 
     513           0 :     UErrorCode status = U_ZERO_ERROR;
     514             :     UChar tzIDKey[ZID_KEY_MAX + 1];
     515           0 :     int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
     516           0 :     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
     517           0 :     tzIDKey[tzIDKeyLen] = 0;
     518             : 
     519           0 :     const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
     520             : 
     521           0 :     if (locname != NULL) {
     522             :         // gEmpty indicate the name is not available
     523           0 :         if (locname == gEmpty) {
     524           0 :             return NULL;
     525             :         }
     526           0 :         return locname;
     527             :     }
     528             : 
     529             :     // Construct location name
     530           0 :     UnicodeString name;
     531           0 :     UnicodeString usCountryCode;
     532           0 :     UBool isPrimary = FALSE;
     533             : 
     534           0 :     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
     535             : 
     536           0 :     if (!usCountryCode.isEmpty()) {
     537           0 :         if (isPrimary) {
     538             :             // If this is the primary zone in the country, use the country name.
     539             :             char countryCode[ULOC_COUNTRY_CAPACITY];
     540           0 :             U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
     541           0 :             int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
     542           0 :             countryCode[ccLen] = 0;
     543             : 
     544           0 :             UnicodeString country;
     545           0 :             fLocaleDisplayNames->regionDisplayName(countryCode, country);
     546           0 :             fRegionFormat.format(country, name, status);
     547             :         } else {
     548             :             // If this is not the primary zone in the country,
     549             :             // use the exemplar city name.
     550             : 
     551             :             // getExemplarLocationName should retur non-empty string
     552             :             // if the time zone is associated with a region
     553             : 
     554           0 :             UnicodeString city;
     555           0 :             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
     556           0 :             fRegionFormat.format(city, name, status);
     557             :         }
     558           0 :         if (U_FAILURE(status)) {
     559           0 :             return NULL;
     560             :         }
     561             :     }
     562             : 
     563           0 :     locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
     564           0 :     if (U_SUCCESS(status)) {
     565             :         // Cache the result
     566           0 :         const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
     567           0 :         U_ASSERT(cacheID != NULL);
     568           0 :         if (locname == NULL) {
     569             :             // gEmpty to indicate - no location name available
     570           0 :             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
     571             :         } else {
     572           0 :             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
     573           0 :             if (U_FAILURE(status)) {
     574           0 :                 locname = NULL;
     575             :             } else {
     576             :                 // put the name info into the trie
     577           0 :                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
     578           0 :                 if (nameinfo != NULL) {
     579           0 :                     nameinfo->type = UTZGNM_LOCATION;
     580           0 :                     nameinfo->tzID = cacheID;
     581           0 :                     fGNamesTrie.put(locname, nameinfo, status);
     582             :                 }
     583             :             }
     584             :         }
     585             :     }
     586             : 
     587           0 :     return locname;
     588             : }
     589             : 
     590             : UnicodeString&
     591           0 : TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
     592           0 :     U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
     593           0 :     name.setToBogus();
     594             : 
     595           0 :     const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
     596           0 :     if (uID == NULL) {
     597           0 :         return name;
     598             :     }
     599             : 
     600           0 :     UnicodeString tzID(TRUE, uID, -1);
     601             : 
     602             :     // Try to get a name from time zone first
     603           0 :     UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
     604           0 :     fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
     605             : 
     606           0 :     if (!name.isEmpty()) {
     607           0 :         return name;
     608             :     }
     609             : 
     610             :     // Try meta zone
     611             :     UChar mzIDBuf[32];
     612           0 :     UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
     613           0 :     fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
     614           0 :     if (!mzID.isEmpty()) {
     615           0 :         UErrorCode status = U_ZERO_ERROR;
     616           0 :         UBool useStandard = FALSE;
     617             :         int32_t raw, sav;
     618             :         UChar tmpNameBuf[64];
     619             : 
     620           0 :         tz.getOffset(date, FALSE, raw, sav, status);
     621           0 :         if (U_FAILURE(status)) {
     622           0 :             return name;
     623             :         }
     624             : 
     625           0 :         if (sav == 0) {
     626           0 :             useStandard = TRUE;
     627             : 
     628           0 :             TimeZone *tmptz = tz.clone();
     629             :             // Check if the zone actually uses daylight saving time around the time
     630           0 :             BasicTimeZone *btz = NULL;
     631           0 :             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
     632           0 :                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
     633           0 :                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
     634           0 :                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
     635           0 :                 btz = (BasicTimeZone*)tmptz;
     636             :             }
     637             : 
     638           0 :             if (btz != NULL) {
     639           0 :                 TimeZoneTransition before;
     640           0 :                 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
     641           0 :                 if (beforTrs
     642           0 :                         && (date - before.getTime() < kDstCheckRange)
     643           0 :                         && before.getFrom()->getDSTSavings() != 0) {
     644           0 :                     useStandard = FALSE;
     645             :                 } else {
     646           0 :                     TimeZoneTransition after;
     647           0 :                     UBool afterTrs = btz->getNextTransition(date, FALSE, after);
     648           0 :                     if (afterTrs
     649           0 :                             && (after.getTime() - date < kDstCheckRange)
     650           0 :                             && after.getTo()->getDSTSavings() != 0) {
     651           0 :                         useStandard = FALSE;
     652             :                     }
     653             :                 }
     654             :             } else {
     655             :                 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
     656             :                 // We may get a wrong answer in edge case, but it should practically work OK.
     657           0 :                 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
     658           0 :                 if (sav != 0) {
     659           0 :                     useStandard = FALSE;
     660             :                 } else {
     661           0 :                     tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
     662           0 :                     if (sav != 0){
     663           0 :                         useStandard = FALSE;
     664             :                     }
     665             :                 }
     666           0 :                 if (U_FAILURE(status)) {
     667           0 :                     delete tmptz;
     668           0 :                     return name;
     669             :                 }
     670             :             }
     671           0 :             delete tmptz;
     672             :         }
     673           0 :         if (useStandard) {
     674             :             UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
     675           0 :                 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
     676           0 :             UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
     677           0 :             fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
     678           0 :             if (!stdName.isEmpty()) {
     679           0 :                 name.setTo(stdName);
     680             : 
     681             :                 // TODO: revisit this issue later
     682             :                 // In CLDR, a same display name is used for both generic and standard
     683             :                 // for some meta zones in some locales.  This looks like a data bugs.
     684             :                 // For now, we check if the standard name is different from its generic
     685             :                 // name below.
     686             :                 UChar genNameBuf[64];
     687           0 :                 UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
     688           0 :                 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
     689           0 :                 if (stdName.caseCompare(mzGenericName, 0) == 0) {
     690           0 :                     name.setToBogus();
     691             :                 }
     692             :             }
     693             :         }
     694           0 :         if (name.isEmpty()) {
     695             :             // Get a name from meta zone
     696           0 :             UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
     697           0 :             fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
     698           0 :             if (!mzName.isEmpty()) {
     699             :                 // Check if we need to use a partial location format.
     700             :                 // This check is done by comparing offset with the meta zone's
     701             :                 // golden zone at the given date.
     702             :                 UChar idBuf[32];
     703           0 :                 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
     704           0 :                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
     705           0 :                 if (!goldenID.isEmpty() && goldenID != tzID) {
     706           0 :                     TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
     707             :                     int32_t raw1, sav1;
     708             : 
     709             :                     // Check offset in the golden zone with wall time.
     710             :                     // With getOffset(date, false, offsets1),
     711             :                     // you may get incorrect results because of time overlap at DST->STD
     712             :                     // transition.
     713           0 :                     goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
     714           0 :                     delete goldenZone;
     715           0 :                     if (U_SUCCESS(status)) {
     716           0 :                         if (raw != raw1 || sav != sav1) {
     717             :                             // Now we need to use a partial location format
     718           0 :                             getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
     719             :                         } else {
     720           0 :                             name.setTo(mzName);
     721             :                         }
     722             :                     }
     723             :                 } else {
     724           0 :                     name.setTo(mzName);
     725             :                 }
     726             :             }
     727             :         }
     728             :     }
     729           0 :     return name;
     730             : }
     731             : 
     732             : UnicodeString&
     733           0 : TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
     734             :                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
     735             :                         UnicodeString& name) const {
     736           0 :     name.setToBogus();
     737           0 :     if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
     738           0 :         return name;
     739             :     }
     740             : 
     741           0 :     const UChar *uplname = NULL;
     742           0 :     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
     743           0 :     umtx_lock(&gLock);
     744             :     {
     745           0 :         uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
     746             :     }
     747           0 :     umtx_unlock(&gLock);
     748             : 
     749           0 :     if (uplname == NULL) {
     750           0 :         name.setToBogus();
     751             :     } else {
     752           0 :         name.setTo(TRUE, uplname, -1);
     753             :     }
     754           0 :     return name;
     755             : }
     756             : 
     757             : /*
     758             :  * This method updates the cache and must be called with a lock
     759             :  */
     760             : const UChar*
     761           0 : TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
     762             :                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
     763           0 :     U_ASSERT(!tzCanonicalID.isEmpty());
     764           0 :     U_ASSERT(!mzID.isEmpty());
     765           0 :     U_ASSERT(!mzDisplayName.isEmpty());
     766             : 
     767             :     PartialLocationKey key;
     768           0 :     key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
     769           0 :     key.mzID = ZoneMeta::findMetaZoneID(mzID);
     770           0 :     key.isLong = isLong;
     771           0 :     U_ASSERT(key.tzID != NULL && key.mzID != NULL);
     772             : 
     773           0 :     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
     774           0 :     if (uplname != NULL) {
     775           0 :         return uplname;
     776             :     }
     777             : 
     778           0 :     UnicodeString location;
     779           0 :     UnicodeString usCountryCode;
     780           0 :     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
     781           0 :     if (!usCountryCode.isEmpty()) {
     782             :         char countryCode[ULOC_COUNTRY_CAPACITY];
     783           0 :         U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
     784           0 :         int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
     785           0 :         countryCode[ccLen] = 0;
     786             : 
     787           0 :         UnicodeString regionalGolden;
     788           0 :         fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
     789           0 :         if (tzCanonicalID == regionalGolden) {
     790             :             // Use country name
     791           0 :             fLocaleDisplayNames->regionDisplayName(countryCode, location);
     792             :         } else {
     793             :             // Otherwise, use exemplar city name
     794           0 :             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
     795             :         }
     796             :     } else {
     797           0 :         fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
     798           0 :         if (location.isEmpty()) {
     799             :             // This could happen when the time zone is not associated with a country,
     800             :             // and its ID is not hierarchical, for example, CST6CDT.
     801             :             // We use the canonical ID itself as the location for this case.
     802           0 :             location.setTo(tzCanonicalID);
     803             :         }
     804             :     }
     805             : 
     806           0 :     UErrorCode status = U_ZERO_ERROR;
     807           0 :     UnicodeString name;
     808           0 :     fFallbackFormat.format(location, mzDisplayName, name, status);
     809           0 :     if (U_FAILURE(status)) {
     810           0 :         return NULL;
     811             :     }
     812             : 
     813           0 :     uplname = fStringPool.get(name, status);
     814           0 :     if (U_SUCCESS(status)) {
     815             :         // Add the name to cache
     816           0 :         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
     817           0 :         if (cacheKey != NULL) {
     818           0 :             cacheKey->tzID = key.tzID;
     819           0 :             cacheKey->mzID = key.mzID;
     820           0 :             cacheKey->isLong = key.isLong;
     821           0 :             uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
     822           0 :             if (U_FAILURE(status)) {
     823           0 :                 uprv_free(cacheKey);
     824             :             } else {
     825             :                 // put the name to the local trie as well
     826           0 :                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
     827           0 :                 if (nameinfo != NULL) {
     828           0 :                     nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
     829           0 :                     nameinfo->tzID = key.tzID;
     830           0 :                     fGNamesTrie.put(uplname, nameinfo, status);
     831             :                 }
     832             :             }
     833             :         }
     834             :     }
     835           0 :     return uplname;
     836             : }
     837             : 
     838             : /*
     839             :  * This method updates the cache and must be called with a lock,
     840             :  * except initializer.
     841             :  */
     842             : void
     843           0 : TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
     844             :     // load the generic location name
     845           0 :     getGenericLocationName(tzCanonicalID);
     846             : 
     847             :     // partial location names
     848           0 :     UErrorCode status = U_ZERO_ERROR;
     849             : 
     850             :     const UnicodeString *mzID;
     851           0 :     UnicodeString goldenID;
     852           0 :     UnicodeString mzGenName;
     853             :     UTimeZoneNameType genNonLocTypes[] = {
     854             :         UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
     855             :         UTZNM_UNKNOWN /*terminator*/
     856           0 :     };
     857             : 
     858           0 :     StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
     859           0 :     while ((mzID = mzIDs->snext(status))) {
     860           0 :         if (U_FAILURE(status)) {
     861           0 :             break;
     862             :         }
     863             :         // if this time zone is not the golden zone of the meta zone,
     864             :         // partial location name (such as "PT (Los Angeles)") might be
     865             :         // available.
     866           0 :         fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
     867           0 :         if (tzCanonicalID != goldenID) {
     868           0 :             for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
     869           0 :                 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
     870           0 :                 if (!mzGenName.isEmpty()) {
     871             :                     // getPartialLocationName formats a name and put it into the trie
     872           0 :                     getPartialLocationName(tzCanonicalID, *mzID,
     873           0 :                         (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
     874             :                 }
     875             :             }
     876             :         }
     877             :     }
     878           0 :     if (mzIDs != NULL) {
     879           0 :         delete mzIDs;
     880             :     }
     881           0 : }
     882             : 
     883             : int32_t
     884           0 : TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
     885             :         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
     886           0 :     timeType = UTZFMT_TIME_TYPE_UNKNOWN;
     887           0 :     tzID.setToBogus();
     888             : 
     889           0 :     if (U_FAILURE(status)) {
     890           0 :         return 0;
     891             :     }
     892             : 
     893             :     // Find matches in the TimeZoneNames first
     894           0 :     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
     895           0 :     if (U_FAILURE(status)) {
     896           0 :         return 0;
     897             :     }
     898             : 
     899           0 :     int32_t bestMatchLen = 0;
     900           0 :     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
     901           0 :     UnicodeString bestMatchTzID;
     902             :     // UBool isLongStandard = FALSE;   // workaround - see the comments below
     903           0 :     UBool isStandard = FALSE;       // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
     904             : 
     905           0 :     if (tznamesMatches != NULL) {
     906           0 :         UnicodeString mzID;
     907           0 :         for (int32_t i = 0; i < tznamesMatches->size(); i++) {
     908           0 :             int32_t len = tznamesMatches->getMatchLengthAt(i);
     909           0 :             if (len > bestMatchLen) {
     910           0 :                 bestMatchLen = len;
     911           0 :                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
     912             :                     // name for a meta zone
     913           0 :                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
     914           0 :                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
     915             :                     }
     916             :                 }
     917           0 :                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
     918           0 :                 if (U_FAILURE(status)) {
     919           0 :                     break;
     920             :                 }
     921           0 :                 switch (nameType) {
     922             :                 case UTZNM_LONG_STANDARD:
     923             :                     // isLongStandard = TRUE;
     924             :                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
     925           0 :                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.
     926           0 :                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
     927           0 :                     break;
     928             :                 case UTZNM_LONG_DAYLIGHT:
     929             :                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
     930           0 :                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
     931           0 :                     break;
     932             :                 default:
     933           0 :                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
     934             :                 }
     935             :             }
     936             :         }
     937           0 :         delete tznamesMatches;
     938           0 :         if (U_FAILURE(status)) {
     939           0 :             return 0;
     940             :         }
     941             : 
     942           0 :         if (bestMatchLen == (text.length() - start)) {
     943             :             // Full match
     944             : 
     945             :             //tzID.setTo(bestMatchTzID);
     946             :             //timeType = bestMatchTimeType;
     947             :             //return bestMatchLen;
     948             : 
     949             :             // TODO Some time zone uses a same name for the long standard name
     950             :             // and the location name. When the match is a long standard name,
     951             :             // then we need to check if the name is same with the location name.
     952             :             // This is probably a data error or a design bug.
     953             : /*
     954             :             if (!isLongStandard) {
     955             :                 tzID.setTo(bestMatchTzID);
     956             :                 timeType = bestMatchTimeType;
     957             :                 return bestMatchLen;
     958             :             }
     959             : */
     960             :             // TODO The deprecation of commonlyUsed flag introduced the name
     961             :             // conflict not only for long standard names, but short standard names too.
     962             :             // These short names (found in zh_Hant) should be gone once we clean
     963             :             // up CLDR time zone display name data. Once the short name conflict
     964             :             // problem (with location name) is resolved, we should change the condition
     965             :             // below back to the original one above. -Yoshito (2011-09-14)
     966           0 :             if (!isStandard) {
     967           0 :                 tzID.setTo(bestMatchTzID);
     968           0 :                 timeType = bestMatchTimeType;
     969           0 :                 return bestMatchLen;
     970             :             }
     971             :         }
     972             :     }
     973             : 
     974             :     // Find matches in the local trie
     975           0 :     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
     976           0 :     if (U_FAILURE(status)) {
     977           0 :         return 0;
     978             :     }
     979           0 :     if (localMatches != NULL) {
     980           0 :         for (int32_t i = 0; i < localMatches->size(); i++) {
     981           0 :             int32_t len = localMatches->getMatchLength(i);
     982             : 
     983             :             // TODO See the above TODO. We use len >= bestMatchLen
     984             :             // because of the long standard/location name collision
     985             :             // problem. If it is also a location name, carrying
     986             :             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
     987             :             // problem in SimpleDateFormat
     988           0 :             if (len >= bestMatchLen) {
     989           0 :                 bestMatchLen = localMatches->getMatchLength(i);
     990           0 :                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
     991           0 :                 localMatches->getTimeZoneID(i, bestMatchTzID);
     992             :             }
     993             :         }
     994           0 :         delete localMatches;
     995             :     }
     996             : 
     997           0 :     if (bestMatchLen > 0) {
     998           0 :         timeType = bestMatchTimeType;
     999           0 :         tzID.setTo(bestMatchTzID);
    1000             :     }
    1001           0 :     return bestMatchLen;
    1002             : }
    1003             : 
    1004             : TimeZoneGenericNameMatchInfo*
    1005           0 : TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
    1006           0 :     GNameSearchHandler handler(types);
    1007             : 
    1008           0 :     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    1009             : 
    1010           0 :     umtx_lock(&gLock);
    1011             :     {
    1012           0 :         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
    1013             :     }
    1014           0 :     umtx_unlock(&gLock);
    1015             : 
    1016           0 :     if (U_FAILURE(status)) {
    1017           0 :         return NULL;
    1018             :     }
    1019             : 
    1020           0 :     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
    1021             : 
    1022           0 :     int32_t maxLen = 0;
    1023           0 :     UVector *results = handler.getMatches(maxLen);
    1024           0 :     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
    1025             :         // perfect match
    1026           0 :         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
    1027           0 :         if (gmatchInfo == NULL) {
    1028           0 :             status = U_MEMORY_ALLOCATION_ERROR;
    1029           0 :             delete results;
    1030           0 :             return NULL;
    1031             :         }
    1032           0 :         return gmatchInfo;
    1033             :     }
    1034             : 
    1035           0 :     if (results != NULL) {
    1036           0 :         delete results;
    1037             :     }
    1038             : 
    1039             :     // All names are not yet loaded into the local trie.
    1040             :     // Load all available names into the trie. This could be very heavy.
    1041           0 :     umtx_lock(&gLock);
    1042             :     {
    1043           0 :         if (!fGNamesTrieFullyLoaded) {
    1044           0 :             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
    1045           0 :             if (U_SUCCESS(status)) {
    1046             :                 const UnicodeString *tzID;
    1047           0 :                 while ((tzID = tzIDs->snext(status))) {
    1048           0 :                     if (U_FAILURE(status)) {
    1049           0 :                         break;
    1050             :                     }
    1051           0 :                     nonConstThis->loadStrings(*tzID);
    1052             :                 }
    1053             :             }
    1054           0 :             if (tzIDs != NULL) {
    1055           0 :                 delete tzIDs;
    1056             :             }
    1057             : 
    1058           0 :             if (U_SUCCESS(status)) {
    1059           0 :                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
    1060             :             }
    1061             :         }
    1062             :     }
    1063           0 :     umtx_unlock(&gLock);
    1064             : 
    1065           0 :     if (U_FAILURE(status)) {
    1066           0 :         return NULL;
    1067             :     }
    1068             : 
    1069           0 :     umtx_lock(&gLock);
    1070             :     {
    1071             :         // now try it again
    1072           0 :         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
    1073             :     }
    1074           0 :     umtx_unlock(&gLock);
    1075             : 
    1076           0 :     results = handler.getMatches(maxLen);
    1077           0 :     if (results != NULL && maxLen > 0) {
    1078           0 :         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
    1079           0 :         if (gmatchInfo == NULL) {
    1080           0 :             status = U_MEMORY_ALLOCATION_ERROR;
    1081           0 :             delete results;
    1082           0 :             return NULL;
    1083             :         }
    1084             :     }
    1085             : 
    1086           0 :     return gmatchInfo;
    1087             : }
    1088             : 
    1089             : TimeZoneNames::MatchInfoCollection*
    1090           0 : TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
    1091             :     // Check if the target name typs is really in the TimeZoneNames
    1092           0 :     uint32_t nameTypes = 0;
    1093           0 :     if (types & UTZGNM_LONG) {
    1094           0 :         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
    1095             :     }
    1096           0 :     if (types & UTZGNM_SHORT) {
    1097           0 :         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
    1098             :     }
    1099             : 
    1100           0 :     if (types) {
    1101             :         // Find matches in the TimeZoneNames
    1102           0 :         return fTimeZoneNames->find(text, start, nameTypes, status);
    1103             :     }
    1104             : 
    1105           0 :     return NULL;
    1106             : }
    1107             : 
    1108             : typedef struct TZGNCoreRef {
    1109             :     TZGNCore*       obj;
    1110             :     int32_t         refCount;
    1111             :     double          lastAccess;
    1112             : } TZGNCoreRef;
    1113             : 
    1114             : // TZGNCore object cache handling
    1115             : static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
    1116             : static UHashtable *gTZGNCoreCache = NULL;
    1117             : static UBool gTZGNCoreCacheInitialized = FALSE;
    1118             : 
    1119             : // Access count - incremented every time up to SWEEP_INTERVAL,
    1120             : // then reset to 0
    1121             : static int32_t gAccessCount = 0;
    1122             : 
    1123             : // Interval for calling the cache sweep function - every 100 times
    1124             : #define SWEEP_INTERVAL 100
    1125             : 
    1126             : // Cache expiration in millisecond. When a cached entry is no
    1127             : // longer referenced and exceeding this threshold since last
    1128             : // access time, then the cache entry will be deleted by the sweep
    1129             : // function. For now, 3 minutes.
    1130             : #define CACHE_EXPIRATION 180000.0
    1131             : 
    1132             : U_CDECL_BEGIN
    1133             : /**
    1134             :  * Cleanup callback func
    1135             :  */
    1136           0 : static UBool U_CALLCONV tzgnCore_cleanup(void)
    1137             : {
    1138           0 :     if (gTZGNCoreCache != NULL) {
    1139           0 :         uhash_close(gTZGNCoreCache);
    1140           0 :         gTZGNCoreCache = NULL;
    1141             :     }
    1142           0 :     gTZGNCoreCacheInitialized = FALSE;
    1143           0 :     return TRUE;
    1144             : }
    1145             : 
    1146             : /**
    1147             :  * Deleter for TZGNCoreRef
    1148             :  */
    1149             : static void U_CALLCONV
    1150           0 : deleteTZGNCoreRef(void *obj) {
    1151           0 :     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
    1152           0 :     delete (icu::TZGNCore*) entry->obj;
    1153           0 :     uprv_free(entry);
    1154           0 : }
    1155             : U_CDECL_END
    1156             : 
    1157             : /**
    1158             :  * Function used for removing unreferrenced cache entries exceeding
    1159             :  * the expiration time. This function must be called with in the mutex
    1160             :  * block.
    1161             :  */
    1162           0 : static void sweepCache() {
    1163           0 :     int32_t pos = UHASH_FIRST;
    1164             :     const UHashElement* elem;
    1165           0 :     double now = (double)uprv_getUTCtime();
    1166             : 
    1167           0 :     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
    1168           0 :         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
    1169           0 :         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
    1170             :             // delete this entry
    1171           0 :             uhash_removeElement(gTZGNCoreCache, elem);
    1172             :         }
    1173             :     }
    1174           0 : }
    1175             : 
    1176           0 : TimeZoneGenericNames::TimeZoneGenericNames()
    1177           0 : : fRef(0) {
    1178           0 : }
    1179             : 
    1180           0 : TimeZoneGenericNames::~TimeZoneGenericNames() {
    1181           0 :     umtx_lock(&gTZGNLock);
    1182             :     {
    1183           0 :         U_ASSERT(fRef->refCount > 0);
    1184             :         // Just decrement the reference count
    1185           0 :         fRef->refCount--;
    1186             :     }
    1187           0 :     umtx_unlock(&gTZGNLock);
    1188           0 : }
    1189             : 
    1190             : TimeZoneGenericNames*
    1191           0 : TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
    1192           0 :     if (U_FAILURE(status)) {
    1193           0 :         return NULL;
    1194             :     }
    1195           0 :     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
    1196           0 :     if (instance == NULL) {
    1197           0 :         status = U_MEMORY_ALLOCATION_ERROR;
    1198           0 :         return NULL;
    1199             :     }
    1200             : 
    1201           0 :     TZGNCoreRef *cacheEntry = NULL;
    1202             :     {
    1203           0 :         Mutex lock(&gTZGNLock);
    1204             : 
    1205           0 :         if (!gTZGNCoreCacheInitialized) {
    1206             :             // Create empty hashtable
    1207           0 :             gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
    1208           0 :             if (U_SUCCESS(status)) {
    1209           0 :                 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
    1210           0 :                 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
    1211           0 :                 gTZGNCoreCacheInitialized = TRUE;
    1212           0 :                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
    1213             :             }
    1214             :         }
    1215           0 :         if (U_FAILURE(status)) {
    1216           0 :             return NULL;
    1217             :         }
    1218             : 
    1219             :         // Check the cache, if not available, create new one and cache
    1220           0 :         const char *key = locale.getName();
    1221           0 :         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
    1222           0 :         if (cacheEntry == NULL) {
    1223           0 :             TZGNCore *tzgnCore = NULL;
    1224           0 :             char *newKey = NULL;
    1225             : 
    1226           0 :             tzgnCore = new TZGNCore(locale, status);
    1227           0 :             if (tzgnCore == NULL) {
    1228           0 :                 status = U_MEMORY_ALLOCATION_ERROR;
    1229             :             }
    1230           0 :             if (U_SUCCESS(status)) {
    1231           0 :                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
    1232           0 :                 if (newKey == NULL) {
    1233           0 :                     status = U_MEMORY_ALLOCATION_ERROR;
    1234             :                 } else {
    1235           0 :                     uprv_strcpy(newKey, key);
    1236             :                 }
    1237             :             }
    1238           0 :             if (U_SUCCESS(status)) {
    1239           0 :                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
    1240           0 :                 if (cacheEntry == NULL) {
    1241           0 :                     status = U_MEMORY_ALLOCATION_ERROR;
    1242             :                 } else {
    1243           0 :                     cacheEntry->obj = tzgnCore;
    1244           0 :                     cacheEntry->refCount = 1;
    1245           0 :                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
    1246             : 
    1247           0 :                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
    1248             :                 }
    1249             :             }
    1250           0 :             if (U_FAILURE(status)) {
    1251           0 :                 if (tzgnCore != NULL) {
    1252           0 :                     delete tzgnCore;
    1253             :                 }
    1254           0 :                 if (newKey != NULL) {
    1255           0 :                     uprv_free(newKey);
    1256             :                 }
    1257           0 :                 if (cacheEntry != NULL) {
    1258           0 :                     uprv_free(cacheEntry);
    1259             :                 }
    1260           0 :                 cacheEntry = NULL;
    1261             :             }
    1262             :         } else {
    1263             :             // Update the reference count
    1264           0 :             cacheEntry->refCount++;
    1265           0 :             cacheEntry->lastAccess = (double)uprv_getUTCtime();
    1266             :         }
    1267           0 :         gAccessCount++;
    1268           0 :         if (gAccessCount >= SWEEP_INTERVAL) {
    1269             :             // sweep
    1270           0 :             sweepCache();
    1271           0 :             gAccessCount = 0;
    1272             :         }
    1273             :     }  // End of mutex locked block
    1274             : 
    1275           0 :     if (cacheEntry == NULL) {
    1276           0 :         delete instance;
    1277           0 :         return NULL;
    1278             :     }
    1279             : 
    1280           0 :     instance->fRef = cacheEntry;
    1281           0 :     return instance;
    1282             : }
    1283             : 
    1284             : UBool
    1285           0 : TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
    1286             :     // Just compare if the other object also use the same
    1287             :     // ref entry
    1288           0 :     return fRef == other.fRef;
    1289             : }
    1290             : 
    1291             : TimeZoneGenericNames*
    1292           0 : TimeZoneGenericNames::clone() const {
    1293           0 :     TimeZoneGenericNames* other = new TimeZoneGenericNames();
    1294           0 :     if (other) {
    1295           0 :         umtx_lock(&gTZGNLock);
    1296             :         {
    1297             :             // Just increments the reference count
    1298           0 :             fRef->refCount++;
    1299           0 :             other->fRef = fRef;
    1300             :         }
    1301           0 :         umtx_unlock(&gTZGNLock);
    1302             :     }
    1303           0 :     return other;
    1304             : }
    1305             : 
    1306             : UnicodeString&
    1307           0 : TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
    1308             :                         UDate date, UnicodeString& name) const {
    1309           0 :     return fRef->obj->getDisplayName(tz, type, date, name);
    1310             : }
    1311             : 
    1312             : UnicodeString&
    1313           0 : TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
    1314           0 :     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
    1315             : }
    1316             : 
    1317             : int32_t
    1318           0 : TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    1319             :         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
    1320           0 :     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
    1321             : }
    1322             : 
    1323             : U_NAMESPACE_END
    1324             : #endif

Generated by: LCOV version 1.13