tzgnames.cpp 43 KB

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