2011-05-03 14:59:01 +00:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2013-01-04 22:26:42 +00:00
* Copyright ( C ) 2011 - 2013 , International Business Machines Corporation and
2011-07-07 18:46:19 +00:00
* others . All Rights Reserved .
2011-05-03 14:59:01 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# include "unicode/utypes.h"
# if !UCONFIG_NO_FORMATTING
# include "tzgnames.h"
# include "unicode/basictz.h"
# include "unicode/locdspnm.h"
# include "unicode/msgfmt.h"
# include "unicode/rbtz.h"
# include "unicode/simpletz.h"
# include "unicode/vtzone.h"
# include "cmemory.h"
# include "cstring.h"
2013-05-31 23:50:15 +00:00
# include "mutex.h"
2011-05-03 14:59:01 +00:00
# include "uhash.h"
# include "uassert.h"
# include "umutex.h"
# include "uresimp.h"
# include "ureslocs.h"
# include "zonemeta.h"
# include "tznames_impl.h"
# include "olsontz.h"
2012-02-21 11:06:50 +00:00
# include "ucln_in.h"
2011-05-03 14:59:01 +00:00
U_NAMESPACE_BEGIN
# define ZID_KEY_MAX 128
static const char gZoneStrings [ ] = " zoneStrings " ;
static const char gRegionFormatTag [ ] = " regionFormat " ;
static const char gFallbackFormatTag [ ] = " fallbackFormat " ;
static const UChar gEmpty [ ] = { 0x00 } ;
static const UChar gDefRegionPattern [ ] = { 0x7B , 0x30 , 0x7D , 0x00 } ; // "{0}"
static const UChar gDefFallbackPattern [ ] = { 0x7B , 0x31 , 0x7D , 0x20 , 0x28 , 0x7B , 0x30 , 0x7D , 0x29 , 0x00 } ; // "{1} ({0})"
static const double kDstCheckRange = ( double ) 184 * U_MILLIS_PER_DAY ;
U_CDECL_BEGIN
typedef struct PartialLocationKey {
const UChar * tzID ;
const UChar * mzID ;
UBool isLong ;
} PartialLocationKey ;
/**
* Hash function for partial location name hash key
*/
static int32_t U_CALLCONV
hashPartialLocationKey ( const UHashTok key ) {
// <tzID>&<mzID>#[L|S]
PartialLocationKey * p = ( PartialLocationKey * ) key . pointer ;
UnicodeString str ( p - > tzID ) ;
str . append ( ( UChar ) 0x26 )
2011-07-07 18:46:19 +00:00
. append ( p - > mzID , - 1 )
2011-05-03 14:59:01 +00:00
. append ( ( UChar ) 0x23 )
. append ( ( UChar ) ( p - > isLong ? 0x4C : 0x53 ) ) ;
2011-06-03 05:23:57 +00:00
return str . hashCode ( ) ;
2011-05-03 14:59:01 +00:00
}
/**
* Comparer for partial location name hash key
*/
static UBool U_CALLCONV
comparePartialLocationKey ( const UHashTok key1 , const UHashTok key2 ) {
PartialLocationKey * p1 = ( PartialLocationKey * ) key1 . pointer ;
PartialLocationKey * p2 = ( PartialLocationKey * ) key2 . pointer ;
if ( p1 = = p2 ) {
return TRUE ;
}
if ( p1 = = NULL | | p2 = = NULL ) {
return FALSE ;
}
// We just check identity of tzID/mzID
return ( p1 - > tzID = = p2 - > tzID & & p1 - > mzID = = p2 - > mzID & & p1 - > isLong = = p2 - > isLong ) ;
}
/**
* Deleter for GNameInfo
*/
static void U_CALLCONV
deleteGNameInfo ( void * obj ) {
uprv_free ( obj ) ;
}
/**
* GNameInfo stores zone name information in the local trie
*/
typedef struct GNameInfo {
UTimeZoneGenericNameType type ;
const UChar * tzID ;
} ZNameInfo ;
/**
* GMatchInfo stores zone name match information used by find method
*/
typedef struct GMatchInfo {
const GNameInfo * gnameInfo ;
int32_t matchLength ;
2012-02-21 11:06:50 +00:00
UTimeZoneFormatTimeType timeType ;
2011-05-03 14:59:01 +00:00
} ZMatchInfo ;
U_CDECL_END
// ---------------------------------------------------
// The class stores time zone generic name match information
// ---------------------------------------------------
2012-02-21 11:06:50 +00:00
class TimeZoneGenericNameMatchInfo : public UMemory {
public :
TimeZoneGenericNameMatchInfo ( UVector * matches ) ;
~ TimeZoneGenericNameMatchInfo ( ) ;
int32_t size ( ) const ;
UTimeZoneGenericNameType getGenericNameType ( int32_t index ) const ;
int32_t getMatchLength ( int32_t index ) const ;
UnicodeString & getTimeZoneID ( int32_t index , UnicodeString & tzID ) const ;
private :
UVector * fMatches ; // vector of MatchEntry
} ;
2011-05-03 14:59:01 +00:00
TimeZoneGenericNameMatchInfo : : TimeZoneGenericNameMatchInfo ( UVector * matches )
: fMatches ( matches ) {
}
TimeZoneGenericNameMatchInfo : : ~ TimeZoneGenericNameMatchInfo ( ) {
if ( fMatches ! = NULL ) {
delete fMatches ;
}
}
int32_t
TimeZoneGenericNameMatchInfo : : size ( ) const {
if ( fMatches = = NULL ) {
return 0 ;
}
return fMatches - > size ( ) ;
}
UTimeZoneGenericNameType
TimeZoneGenericNameMatchInfo : : getGenericNameType ( int32_t index ) const {
GMatchInfo * minfo = ( GMatchInfo * ) fMatches - > elementAt ( index ) ;
if ( minfo ! = NULL ) {
return static_cast < UTimeZoneGenericNameType > ( minfo - > gnameInfo - > type ) ;
}
return UTZGNM_UNKNOWN ;
}
int32_t
TimeZoneGenericNameMatchInfo : : getMatchLength ( int32_t index ) const {
ZMatchInfo * minfo = ( ZMatchInfo * ) fMatches - > elementAt ( index ) ;
if ( minfo ! = NULL ) {
return minfo - > matchLength ;
}
return - 1 ;
}
UnicodeString &
TimeZoneGenericNameMatchInfo : : getTimeZoneID ( int32_t index , UnicodeString & tzID ) const {
GMatchInfo * minfo = ( GMatchInfo * ) fMatches - > elementAt ( index ) ;
if ( minfo ! = NULL & & minfo - > gnameInfo - > tzID ! = NULL ) {
tzID . setTo ( TRUE , minfo - > gnameInfo - > tzID , - 1 ) ;
} else {
tzID . setToBogus ( ) ;
}
return tzID ;
}
// ---------------------------------------------------
// GNameSearchHandler
// ---------------------------------------------------
class GNameSearchHandler : public TextTrieMapSearchResultHandler {
public :
GNameSearchHandler ( uint32_t types ) ;
virtual ~ GNameSearchHandler ( ) ;
UBool handleMatch ( int32_t matchLength , const CharacterNode * node , UErrorCode & status ) ;
UVector * getMatches ( int32_t & maxMatchLen ) ;
private :
uint32_t fTypes ;
UVector * fResults ;
int32_t fMaxMatchLen ;
} ;
GNameSearchHandler : : GNameSearchHandler ( uint32_t types )
: fTypes ( types ) , fResults ( NULL ) , fMaxMatchLen ( 0 ) {
}
GNameSearchHandler : : ~ GNameSearchHandler ( ) {
if ( fResults ! = NULL ) {
delete fResults ;
}
}
UBool
GNameSearchHandler : : handleMatch ( int32_t matchLength , const CharacterNode * node , UErrorCode & status ) {
if ( U_FAILURE ( status ) ) {
return FALSE ;
}
if ( node - > hasValues ( ) ) {
int32_t valuesCount = node - > countValues ( ) ;
for ( int32_t i = 0 ; i < valuesCount ; i + + ) {
GNameInfo * nameinfo = ( ZNameInfo * ) node - > getValue ( i ) ;
if ( nameinfo = = NULL ) {
break ;
}
if ( ( nameinfo - > type & fTypes ) ! = 0 ) {
// matches a requested type
if ( fResults = = NULL ) {
2011-06-03 05:23:57 +00:00
fResults = new UVector ( uprv_free , NULL , status ) ;
2011-05-03 14:59:01 +00:00
if ( fResults = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
}
}
if ( U_SUCCESS ( status ) ) {
2011-11-14 19:32:51 +00:00
U_ASSERT ( fResults ! = NULL ) ;
2011-05-03 14:59:01 +00:00
GMatchInfo * gmatch = ( GMatchInfo * ) uprv_malloc ( sizeof ( GMatchInfo ) ) ;
if ( gmatch = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
} else {
// add the match to the vector
gmatch - > gnameInfo = nameinfo ;
gmatch - > matchLength = matchLength ;
gmatch - > timeType = UTZFMT_TIME_TYPE_UNKNOWN ;
fResults - > addElement ( gmatch , status ) ;
if ( U_FAILURE ( status ) ) {
uprv_free ( gmatch ) ;
} else {
if ( matchLength > fMaxMatchLen ) {
fMaxMatchLen = matchLength ;
}
}
}
}
}
}
}
return TRUE ;
}
UVector *
GNameSearchHandler : : getMatches ( int32_t & maxMatchLen ) {
// give the ownership to the caller
UVector * results = fResults ;
maxMatchLen = fMaxMatchLen ;
// reset
fResults = NULL ;
fMaxMatchLen = 0 ;
return results ;
}
2012-10-05 21:22:02 +00:00
static UMutex gLock = U_MUTEX_INITIALIZER ;
2012-02-21 11:06:50 +00:00
class TZGNCore : public UMemory {
public :
TZGNCore ( const Locale & locale , UErrorCode & status ) ;
virtual ~ TZGNCore ( ) ;
UnicodeString & getDisplayName ( const TimeZone & tz , UTimeZoneGenericNameType type ,
UDate date , UnicodeString & name ) const ;
UnicodeString & getGenericLocationName ( const UnicodeString & tzCanonicalID , UnicodeString & name ) const ;
int32_t findBestMatch ( const UnicodeString & text , int32_t start , uint32_t types ,
UnicodeString & tzID , UTimeZoneFormatTimeType & timeType , UErrorCode & status ) const ;
private :
Locale fLocale ;
const TimeZoneNames * fTimeZoneNames ;
UHashtable * fLocationNamesMap ;
UHashtable * fPartialLocationNamesMap ;
MessageFormat * fRegionFormat ;
MessageFormat * fFallbackFormat ;
LocaleDisplayNames * fLocaleDisplayNames ;
ZNStringPool fStringPool ;
TextTrieMap fGNamesTrie ;
UBool fGNamesTrieFullyLoaded ;
char fTargetRegion [ ULOC_COUNTRY_CAPACITY ] ;
void initialize ( const Locale & locale , UErrorCode & status ) ;
void cleanup ( ) ;
void loadStrings ( const UnicodeString & tzCanonicalID ) ;
const UChar * getGenericLocationName ( const UnicodeString & tzCanonicalID ) ;
UnicodeString & formatGenericNonLocationName ( const TimeZone & tz , UTimeZoneGenericNameType type ,
UDate date , UnicodeString & name ) const ;
UnicodeString & getPartialLocationName ( const UnicodeString & tzCanonicalID ,
const UnicodeString & mzID , UBool isLong , const UnicodeString & mzDisplayName ,
UnicodeString & name ) const ;
const UChar * getPartialLocationName ( const UnicodeString & tzCanonicalID ,
const UnicodeString & mzID , UBool isLong , const UnicodeString & mzDisplayName ) ;
TimeZoneGenericNameMatchInfo * findLocal ( const UnicodeString & text , int32_t start , uint32_t types , UErrorCode & status ) const ;
TimeZoneNames : : MatchInfoCollection * findTimeZoneNames ( const UnicodeString & text , int32_t start , uint32_t types , UErrorCode & status ) const ;
} ;
2011-05-03 14:59:01 +00:00
// ---------------------------------------------------
2012-02-21 11:06:50 +00:00
// TZGNCore - core implmentation of TimeZoneGenericNames
2011-05-03 14:59:01 +00:00
//
// TimeZoneGenericNames is parallel to TimeZoneNames,
// but handles run-time generated time zone names.
// This is the main part of this module.
// ---------------------------------------------------
2012-02-21 11:06:50 +00:00
TZGNCore : : TZGNCore ( const Locale & locale , UErrorCode & status )
2011-05-03 14:59:01 +00:00
: fLocale ( locale ) ,
fTimeZoneNames ( NULL ) ,
fLocationNamesMap ( NULL ) ,
fPartialLocationNamesMap ( NULL ) ,
fRegionFormat ( NULL ) ,
fFallbackFormat ( NULL ) ,
fLocaleDisplayNames ( NULL ) ,
fStringPool ( status ) ,
fGNamesTrie ( TRUE , deleteGNameInfo ) ,
fGNamesTrieFullyLoaded ( FALSE ) {
initialize ( locale , status ) ;
}
2012-02-21 11:06:50 +00:00
TZGNCore : : ~ TZGNCore ( ) {
2011-05-03 14:59:01 +00:00
cleanup ( ) ;
}
void
2012-02-21 11:06:50 +00:00
TZGNCore : : initialize ( const Locale & locale , UErrorCode & status ) {
2011-05-03 14:59:01 +00:00
if ( U_FAILURE ( status ) ) {
return ;
}
// TimeZoneNames
fTimeZoneNames = TimeZoneNames : : createInstance ( locale , status ) ;
if ( U_FAILURE ( status ) ) {
return ;
}
// Initialize format patterns
UnicodeString rpat ( TRUE , gDefRegionPattern , - 1 ) ;
UnicodeString fpat ( TRUE , gDefFallbackPattern , - 1 ) ;
UErrorCode tmpsts = U_ZERO_ERROR ; // OK with fallback warning..
UResourceBundle * zoneStrings = ures_open ( U_ICUDATA_ZONE , locale . getName ( ) , & tmpsts ) ;
zoneStrings = ures_getByKeyWithFallback ( zoneStrings , gZoneStrings , zoneStrings , & tmpsts ) ;
if ( U_SUCCESS ( tmpsts ) ) {
const UChar * regionPattern = ures_getStringByKeyWithFallback ( zoneStrings , gRegionFormatTag , NULL , & tmpsts ) ;
if ( U_SUCCESS ( tmpsts ) & & u_strlen ( regionPattern ) > 0 ) {
2011-07-07 18:46:19 +00:00
rpat . setTo ( regionPattern , - 1 ) ;
2011-05-03 14:59:01 +00:00
}
tmpsts = U_ZERO_ERROR ;
const UChar * fallbackPattern = ures_getStringByKeyWithFallback ( zoneStrings , gFallbackFormatTag , NULL , & tmpsts ) ;
if ( U_SUCCESS ( tmpsts ) & & u_strlen ( fallbackPattern ) > 0 ) {
2011-07-07 18:46:19 +00:00
fpat . setTo ( fallbackPattern , - 1 ) ;
2011-05-03 14:59:01 +00:00
}
}
ures_close ( zoneStrings ) ;
fRegionFormat = new MessageFormat ( rpat , status ) ;
if ( fRegionFormat = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
}
fFallbackFormat = new MessageFormat ( fpat , status ) ;
if ( fFallbackFormat = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
}
if ( U_FAILURE ( status ) ) {
cleanup ( ) ;
return ;
}
// locale display names
fLocaleDisplayNames = LocaleDisplayNames : : createInstance ( locale ) ;
// hash table for names - no key/value deleters
fLocationNamesMap = uhash_open ( uhash_hashUChars , uhash_compareUChars , NULL , & status ) ;
if ( U_FAILURE ( status ) ) {
cleanup ( ) ;
return ;
}
fPartialLocationNamesMap = uhash_open ( hashPartialLocationKey , comparePartialLocationKey , NULL , & status ) ;
if ( U_FAILURE ( status ) ) {
cleanup ( ) ;
return ;
}
2011-06-03 05:23:57 +00:00
uhash_setKeyDeleter ( fPartialLocationNamesMap , uprv_free ) ;
2011-05-03 14:59:01 +00:00
// no value deleter
// target region
const char * region = fLocale . getCountry ( ) ;
int32_t regionLen = uprv_strlen ( region ) ;
if ( regionLen = = 0 ) {
char loc [ ULOC_FULLNAME_CAPACITY ] ;
uloc_addLikelySubtags ( fLocale . getName ( ) , loc , sizeof ( loc ) , & status ) ;
regionLen = uloc_getCountry ( loc , fTargetRegion , sizeof ( fTargetRegion ) , & status ) ;
if ( U_SUCCESS ( status ) ) {
fTargetRegion [ regionLen ] = 0 ;
} else {
cleanup ( ) ;
return ;
}
} else if ( regionLen < ( int32_t ) sizeof ( fTargetRegion ) ) {
uprv_strcpy ( fTargetRegion , region ) ;
} else {
fTargetRegion [ 0 ] = 0 ;
}
// preload generic names for the default zone
TimeZone * tz = TimeZone : : createDefault ( ) ;
const UChar * tzID = ZoneMeta : : getCanonicalCLDRID ( * tz ) ;
if ( tzID ! = NULL ) {
loadStrings ( UnicodeString ( tzID ) ) ;
}
delete tz ;
}
void
2012-02-21 11:06:50 +00:00
TZGNCore : : cleanup ( ) {
2011-05-03 14:59:01 +00:00
if ( fRegionFormat ! = NULL ) {
delete fRegionFormat ;
}
if ( fFallbackFormat ! = NULL ) {
delete fFallbackFormat ;
}
if ( fLocaleDisplayNames ! = NULL ) {
delete fLocaleDisplayNames ;
}
if ( fTimeZoneNames ! = NULL ) {
delete fTimeZoneNames ;
}
uhash_close ( fLocationNamesMap ) ;
uhash_close ( fPartialLocationNamesMap ) ;
}
2012-02-21 11:06:50 +00:00
2011-05-03 14:59:01 +00:00
UnicodeString &
2012-02-21 11:06:50 +00:00
TZGNCore : : getDisplayName ( const TimeZone & tz , UTimeZoneGenericNameType type , UDate date , UnicodeString & name ) const {
2011-05-03 14:59:01 +00:00
name . setToBogus ( ) ;
switch ( type ) {
case UTZGNM_LOCATION :
{
const UChar * tzCanonicalID = ZoneMeta : : getCanonicalCLDRID ( tz ) ;
if ( tzCanonicalID ! = NULL ) {
getGenericLocationName ( UnicodeString ( tzCanonicalID ) , name ) ;
}
}
break ;
case UTZGNM_LONG :
case UTZGNM_SHORT :
formatGenericNonLocationName ( tz , type , date , name ) ;
if ( name . isEmpty ( ) ) {
const UChar * tzCanonicalID = ZoneMeta : : getCanonicalCLDRID ( tz ) ;
if ( tzCanonicalID ! = NULL ) {
getGenericLocationName ( UnicodeString ( tzCanonicalID ) , name ) ;
}
}
break ;
default :
break ;
}
return name ;
}
UnicodeString &
2012-02-21 11:06:50 +00:00
TZGNCore : : getGenericLocationName ( const UnicodeString & tzCanonicalID , UnicodeString & name ) const {
2011-05-03 14:59:01 +00:00
if ( tzCanonicalID . isEmpty ( ) ) {
name . setToBogus ( ) ;
return name ;
}
const UChar * locname = NULL ;
2012-02-21 11:06:50 +00:00
TZGNCore * nonConstThis = const_cast < TZGNCore * > ( this ) ;
2012-10-05 21:22:02 +00:00
umtx_lock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
{
locname = nonConstThis - > getGenericLocationName ( tzCanonicalID ) ;
}
2012-10-05 21:22:02 +00:00
umtx_unlock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
if ( locname = = NULL ) {
name . setToBogus ( ) ;
} else {
2012-02-21 11:06:50 +00:00
name . setTo ( locname , u_strlen ( locname ) ) ;
2011-05-03 14:59:01 +00:00
}
return name ;
}
/*
* This method updates the cache and must be called with a lock
*/
const UChar *
2012-02-21 11:06:50 +00:00
TZGNCore : : getGenericLocationName ( const UnicodeString & tzCanonicalID ) {
2011-05-03 14:59:01 +00:00
U_ASSERT ( ! tzCanonicalID . isEmpty ( ) ) ;
if ( tzCanonicalID . length ( ) > ZID_KEY_MAX ) {
return NULL ;
}
UErrorCode status = U_ZERO_ERROR ;
UChar tzIDKey [ ZID_KEY_MAX + 1 ] ;
int32_t tzIDKeyLen = tzCanonicalID . extract ( tzIDKey , ZID_KEY_MAX + 1 , status ) ;
U_ASSERT ( status = = U_ZERO_ERROR ) ; // already checked length above
tzIDKey [ tzIDKeyLen ] = 0 ;
const UChar * locname = ( const UChar * ) uhash_get ( fLocationNamesMap , tzIDKey ) ;
if ( locname ! = NULL ) {
// gEmpty indicate the name is not available
if ( locname = = gEmpty ) {
return NULL ;
}
return locname ;
}
// Construct location name
UnicodeString name ;
UnicodeString usCountryCode ;
2013-01-04 22:26:42 +00:00
UBool isPrimary = FALSE ;
ZoneMeta : : getCanonicalCountry ( tzCanonicalID , usCountryCode , & isPrimary ) ;
2011-05-03 14:59:01 +00:00
if ( ! usCountryCode . isEmpty ( ) ) {
2013-01-04 22:26:42 +00:00
FieldPosition fpos ;
2011-05-03 14:59:01 +00:00
2013-01-04 22:26:42 +00:00
if ( isPrimary ) {
// If this is the primary zone in the country, use the country name.
char countryCode [ ULOC_COUNTRY_CAPACITY ] ;
U_ASSERT ( usCountryCode . length ( ) < ULOC_COUNTRY_CAPACITY ) ;
int32_t ccLen = usCountryCode . extract ( 0 , usCountryCode . length ( ) , countryCode , sizeof ( countryCode ) , US_INV ) ;
countryCode [ ccLen ] = 0 ;
UnicodeString country ;
fLocaleDisplayNames - > regionDisplayName ( countryCode , country ) ;
2011-05-03 14:59:01 +00:00
Formattable param [ ] = {
Formattable ( country )
} ;
2013-01-04 22:26:42 +00:00
2011-05-03 14:59:01 +00:00
fRegionFormat - > format ( param , 1 , name , fpos , status ) ;
} else {
2013-01-04 22:26:42 +00:00
// If this is not the primary zone in the country,
// use the exemplar city name.
2012-09-11 15:28:29 +00:00
2011-05-03 14:59:01 +00:00
// getExemplarLocationName should retur non-empty string
// if the time zone is associated with a region
2012-09-11 15:28:29 +00:00
2011-05-03 14:59:01 +00:00
UnicodeString city ;
fTimeZoneNames - > getExemplarLocationName ( tzCanonicalID , city ) ;
2012-09-11 15:28:29 +00:00
Formattable param [ ] = {
2011-05-03 14:59:01 +00:00
Formattable ( city ) ,
} ;
2013-01-04 22:26:42 +00:00
2012-09-11 15:28:29 +00:00
fRegionFormat - > format ( param , 1 , name , fpos , status ) ;
2011-05-03 14:59:01 +00:00
}
if ( U_FAILURE ( status ) ) {
return NULL ;
}
}
locname = name . isEmpty ( ) ? NULL : fStringPool . get ( name , status ) ;
if ( U_SUCCESS ( status ) ) {
// Cache the result
const UChar * cacheID = ZoneMeta : : findTimeZoneID ( tzCanonicalID ) ;
U_ASSERT ( cacheID ! = NULL ) ;
if ( locname = = NULL ) {
// gEmpty to indicate - no location name available
uhash_put ( fLocationNamesMap , ( void * ) cacheID , ( void * ) gEmpty , & status ) ;
} else {
uhash_put ( fLocationNamesMap , ( void * ) cacheID , ( void * ) locname , & status ) ;
if ( U_FAILURE ( status ) ) {
locname = NULL ;
} else {
// put the name info into the trie
GNameInfo * nameinfo = ( ZNameInfo * ) uprv_malloc ( sizeof ( GNameInfo ) ) ;
if ( nameinfo ! = NULL ) {
nameinfo - > type = UTZGNM_LOCATION ;
nameinfo - > tzID = cacheID ;
fGNamesTrie . put ( locname , nameinfo , status ) ;
}
}
}
}
return locname ;
}
UnicodeString &
2012-02-21 11:06:50 +00:00
TZGNCore : : formatGenericNonLocationName ( const TimeZone & tz , UTimeZoneGenericNameType type , UDate date , UnicodeString & name ) const {
2011-05-03 14:59:01 +00:00
U_ASSERT ( type = = UTZGNM_LONG | | type = = UTZGNM_SHORT ) ;
name . setToBogus ( ) ;
const UChar * uID = ZoneMeta : : getCanonicalCLDRID ( tz ) ;
if ( uID = = NULL ) {
return name ;
}
UnicodeString tzID ( uID ) ;
// Try to get a name from time zone first
UTimeZoneNameType nameType = ( type = = UTZGNM_LONG ) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC ;
fTimeZoneNames - > getTimeZoneDisplayName ( tzID , nameType , name ) ;
if ( ! name . isEmpty ( ) ) {
return name ;
}
// Try meta zone
UnicodeString mzID ;
fTimeZoneNames - > getMetaZoneID ( tzID , date , mzID ) ;
if ( ! mzID . isEmpty ( ) ) {
UErrorCode status = U_ZERO_ERROR ;
UBool useStandard = FALSE ;
int32_t raw , sav ;
tz . getOffset ( date , FALSE , raw , sav , status ) ;
if ( U_FAILURE ( status ) ) {
return name ;
}
if ( sav = = 0 ) {
useStandard = TRUE ;
TimeZone * tmptz = tz . clone ( ) ;
// Check if the zone actually uses daylight saving time around the time
BasicTimeZone * btz = NULL ;
if ( dynamic_cast < OlsonTimeZone * > ( tmptz ) ! = NULL
| | dynamic_cast < SimpleTimeZone * > ( tmptz ) ! = NULL
| | dynamic_cast < RuleBasedTimeZone * > ( tmptz ) ! = NULL
| | dynamic_cast < VTimeZone * > ( tmptz ) ! = NULL ) {
btz = ( BasicTimeZone * ) tmptz ;
}
if ( btz ! = NULL ) {
TimeZoneTransition before ;
UBool beforTrs = btz - > getPreviousTransition ( date , TRUE , before ) ;
if ( beforTrs
& & ( date - before . getTime ( ) < kDstCheckRange )
& & before . getFrom ( ) - > getDSTSavings ( ) ! = 0 ) {
useStandard = FALSE ;
} else {
TimeZoneTransition after ;
UBool afterTrs = btz - > getNextTransition ( date , FALSE , after ) ;
if ( afterTrs
& & ( after . getTime ( ) - date < kDstCheckRange )
& & after . getTo ( ) - > getDSTSavings ( ) ! = 0 ) {
useStandard = FALSE ;
}
}
} else {
// If not BasicTimeZone... only if the instance is not an ICU's implementation.
// We may get a wrong answer in edge case, but it should practically work OK.
tmptz - > getOffset ( date - kDstCheckRange , FALSE , raw , sav , status ) ;
if ( sav ! = 0 ) {
useStandard = FALSE ;
} else {
tmptz - > getOffset ( date + kDstCheckRange , FALSE , raw , sav , status ) ;
if ( sav ! = 0 ) {
useStandard = FALSE ;
}
}
if ( U_FAILURE ( status ) ) {
delete tmptz ;
return name ;
}
}
delete tmptz ;
}
if ( useStandard ) {
UTimeZoneNameType stdNameType = ( nameType = = UTZNM_LONG_GENERIC )
2011-09-15 20:38:26 +00:00
? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD ;
2011-05-03 14:59:01 +00:00
UnicodeString stdName ;
fTimeZoneNames - > getDisplayName ( tzID , stdNameType , date , stdName ) ;
if ( ! stdName . isEmpty ( ) ) {
name . setTo ( stdName ) ;
// TODO: revisit this issue later
// In CLDR, a same display name is used for both generic and standard
// for some meta zones in some locales. This looks like a data bugs.
// For now, we check if the standard name is different from its generic
// name below.
UnicodeString mzGenericName ;
fTimeZoneNames - > getMetaZoneDisplayName ( mzID , nameType , mzGenericName ) ;
if ( stdName . caseCompare ( mzGenericName , 0 ) = = 0 ) {
name . setToBogus ( ) ;
}
}
}
if ( name . isEmpty ( ) ) {
// Get a name from meta zone
UnicodeString mzName ;
fTimeZoneNames - > getMetaZoneDisplayName ( mzID , nameType , mzName ) ;
if ( ! mzName . isEmpty ( ) ) {
// Check if we need to use a partial location format.
// This check is done by comparing offset with the meta zone's
// golden zone at the given date.
UnicodeString goldenID ;
fTimeZoneNames - > getReferenceZoneID ( mzID , fTargetRegion , goldenID ) ;
if ( ! goldenID . isEmpty ( ) & & goldenID ! = tzID ) {
TimeZone * goldenZone = TimeZone : : createTimeZone ( goldenID ) ;
int32_t raw1 , sav1 ;
// Check offset in the golden zone with wall time.
// With getOffset(date, false, offsets1),
// you may get incorrect results because of time overlap at DST->STD
// transition.
goldenZone - > getOffset ( date + raw + sav , TRUE , raw1 , sav1 , status ) ;
delete goldenZone ;
if ( U_SUCCESS ( status ) ) {
if ( raw ! = raw1 | | sav ! = sav1 ) {
// Now we need to use a partial location format
getPartialLocationName ( tzID , mzID , ( nameType = = UTZNM_LONG_GENERIC ) , mzName , name ) ;
} else {
name . setTo ( mzName ) ;
}
}
} else {
name . setTo ( mzName ) ;
}
}
}
}
return name ;
}
UnicodeString &
2012-02-21 11:06:50 +00:00
TZGNCore : : getPartialLocationName ( const UnicodeString & tzCanonicalID ,
2011-05-03 14:59:01 +00:00
const UnicodeString & mzID , UBool isLong , const UnicodeString & mzDisplayName ,
UnicodeString & name ) const {
name . setToBogus ( ) ;
if ( tzCanonicalID . isEmpty ( ) | | mzID . isEmpty ( ) | | mzDisplayName . isEmpty ( ) ) {
return name ;
}
const UChar * uplname = NULL ;
2012-02-21 11:06:50 +00:00
TZGNCore * nonConstThis = const_cast < TZGNCore * > ( this ) ;
2012-10-05 21:22:02 +00:00
umtx_lock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
{
uplname = nonConstThis - > getPartialLocationName ( tzCanonicalID , mzID , isLong , mzDisplayName ) ;
}
2012-10-05 21:22:02 +00:00
umtx_unlock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
if ( uplname = = NULL ) {
name . setToBogus ( ) ;
} else {
name . setTo ( TRUE , uplname , - 1 ) ;
}
return name ;
}
/*
* This method updates the cache and must be called with a lock
*/
const UChar *
2012-02-21 11:06:50 +00:00
TZGNCore : : getPartialLocationName ( const UnicodeString & tzCanonicalID ,
2011-05-03 14:59:01 +00:00
const UnicodeString & mzID , UBool isLong , const UnicodeString & mzDisplayName ) {
U_ASSERT ( ! tzCanonicalID . isEmpty ( ) ) ;
U_ASSERT ( ! mzID . isEmpty ( ) ) ;
U_ASSERT ( ! mzDisplayName . isEmpty ( ) ) ;
PartialLocationKey key ;
key . tzID = ZoneMeta : : findTimeZoneID ( tzCanonicalID ) ;
key . mzID = ZoneMeta : : findMetaZoneID ( mzID ) ;
key . isLong = isLong ;
U_ASSERT ( key . tzID ! = NULL & & key . mzID ! = NULL ) ;
const UChar * uplname = ( const UChar * ) uhash_get ( fPartialLocationNamesMap , ( void * ) & key ) ;
if ( uplname ! = NULL ) {
return uplname ;
}
UnicodeString location ;
UnicodeString usCountryCode ;
2011-05-26 20:56:15 +00:00
ZoneMeta : : getCanonicalCountry ( tzCanonicalID , usCountryCode ) ;
2011-05-03 14:59:01 +00:00
if ( ! usCountryCode . isEmpty ( ) ) {
char countryCode [ ULOC_COUNTRY_CAPACITY ] ;
U_ASSERT ( usCountryCode . length ( ) < ULOC_COUNTRY_CAPACITY ) ;
int32_t ccLen = usCountryCode . extract ( 0 , usCountryCode . length ( ) , countryCode , sizeof ( countryCode ) , US_INV ) ;
countryCode [ ccLen ] = 0 ;
2011-05-26 20:56:15 +00:00
UnicodeString regionalGolden ;
fTimeZoneNames - > getReferenceZoneID ( mzID , countryCode , regionalGolden ) ;
if ( tzCanonicalID = = regionalGolden ) {
// Use country name
fLocaleDisplayNames - > regionDisplayName ( countryCode , location ) ;
} else {
// Otherwise, use exemplar city name
fTimeZoneNames - > getExemplarLocationName ( tzCanonicalID , location ) ;
}
2011-05-03 14:59:01 +00:00
} else {
fTimeZoneNames - > getExemplarLocationName ( tzCanonicalID , location ) ;
if ( location . isEmpty ( ) ) {
// This could happen when the time zone is not associated with a country,
// and its ID is not hierarchical, for example, CST6CDT.
// We use the canonical ID itself as the location for this case.
location . setTo ( tzCanonicalID ) ;
}
}
UErrorCode status = U_ZERO_ERROR ;
UnicodeString name ;
FieldPosition fpos ;
Formattable param [ ] = {
Formattable ( location ) ,
Formattable ( mzDisplayName )
} ;
fFallbackFormat - > format ( param , 2 , name , fpos , status ) ;
if ( U_FAILURE ( status ) ) {
return NULL ;
}
uplname = fStringPool . get ( name , status ) ;
if ( U_SUCCESS ( status ) ) {
// Add the name to cache
PartialLocationKey * cacheKey = ( PartialLocationKey * ) uprv_malloc ( sizeof ( PartialLocationKey ) ) ;
if ( cacheKey ! = NULL ) {
cacheKey - > tzID = key . tzID ;
cacheKey - > mzID = key . mzID ;
cacheKey - > isLong = key . isLong ;
uhash_put ( fPartialLocationNamesMap , ( void * ) cacheKey , ( void * ) uplname , & status ) ;
if ( U_FAILURE ( status ) ) {
uprv_free ( cacheKey ) ;
} else {
// put the name to the local trie as well
GNameInfo * nameinfo = ( ZNameInfo * ) uprv_malloc ( sizeof ( GNameInfo ) ) ;
if ( nameinfo ! = NULL ) {
nameinfo - > type = isLong ? UTZGNM_LONG : UTZGNM_SHORT ;
nameinfo - > tzID = key . tzID ;
fGNamesTrie . put ( uplname , nameinfo , status ) ;
}
}
}
}
return uplname ;
}
/*
* This method updates the cache and must be called with a lock ,
* except initializer .
*/
void
2012-02-21 11:06:50 +00:00
TZGNCore : : loadStrings ( const UnicodeString & tzCanonicalID ) {
2011-05-03 14:59:01 +00:00
// load the generic location name
getGenericLocationName ( tzCanonicalID ) ;
// partial location names
UErrorCode status = U_ZERO_ERROR ;
const UnicodeString * mzID ;
UnicodeString goldenID ;
UnicodeString mzGenName ;
UTimeZoneNameType genNonLocTypes [ ] = {
UTZNM_LONG_GENERIC , UTZNM_SHORT_GENERIC ,
UTZNM_UNKNOWN /*terminator*/
} ;
StringEnumeration * mzIDs = fTimeZoneNames - > getAvailableMetaZoneIDs ( tzCanonicalID , status ) ;
while ( ( mzID = mzIDs - > snext ( status ) ) ) {
if ( U_FAILURE ( status ) ) {
break ;
}
// if this time zone is not the golden zone of the meta zone,
// partial location name (such as "PT (Los Angeles)") might be
// available.
fTimeZoneNames - > getReferenceZoneID ( * mzID , fTargetRegion , goldenID ) ;
if ( tzCanonicalID ! = goldenID ) {
for ( int32_t i = 0 ; genNonLocTypes [ i ] ! = UTZNM_UNKNOWN ; i + + ) {
fTimeZoneNames - > getMetaZoneDisplayName ( * mzID , genNonLocTypes [ i ] , mzGenName ) ;
if ( ! mzGenName . isEmpty ( ) ) {
// getPartialLocationName formats a name and put it into the trie
getPartialLocationName ( tzCanonicalID , * mzID ,
( genNonLocTypes [ i ] = = UTZNM_LONG_GENERIC ) , mzGenName ) ;
}
}
}
}
if ( mzIDs ! = NULL ) {
delete mzIDs ;
}
}
int32_t
2012-02-21 11:06:50 +00:00
TZGNCore : : findBestMatch ( const UnicodeString & text , int32_t start , uint32_t types ,
UnicodeString & tzID , UTimeZoneFormatTimeType & timeType , UErrorCode & status ) const {
2011-05-03 14:59:01 +00:00
timeType = UTZFMT_TIME_TYPE_UNKNOWN ;
tzID . setToBogus ( ) ;
if ( U_FAILURE ( status ) ) {
return 0 ;
}
// Find matches in the TimeZoneNames first
2012-02-21 11:06:50 +00:00
TimeZoneNames : : MatchInfoCollection * tznamesMatches = findTimeZoneNames ( text , start , types , status ) ;
2011-05-03 14:59:01 +00:00
if ( U_FAILURE ( status ) ) {
return 0 ;
}
int32_t bestMatchLen = 0 ;
2012-02-21 11:06:50 +00:00
UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN ;
2011-05-03 14:59:01 +00:00
UnicodeString bestMatchTzID ;
2012-02-06 19:57:08 +00:00
// UBool isLongStandard = FALSE; // workaround - see the comments below
2011-09-15 20:38:26 +00:00
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
2011-05-03 14:59:01 +00:00
if ( tznamesMatches ! = NULL ) {
UnicodeString mzID ;
for ( int32_t i = 0 ; i < tznamesMatches - > size ( ) ; i + + ) {
2012-02-21 11:06:50 +00:00
int32_t len = tznamesMatches - > getMatchLengthAt ( i ) ;
2011-05-03 14:59:01 +00:00
if ( len > bestMatchLen ) {
bestMatchLen = len ;
2012-02-21 11:06:50 +00:00
if ( ! tznamesMatches - > getTimeZoneIDAt ( i , bestMatchTzID ) ) {
2011-05-03 14:59:01 +00:00
// name for a meta zone
2012-02-21 11:06:50 +00:00
if ( tznamesMatches - > getMetaZoneIDAt ( i , mzID ) ) {
fTimeZoneNames - > getReferenceZoneID ( mzID , fTargetRegion , bestMatchTzID ) ;
}
}
UTimeZoneNameType nameType = tznamesMatches - > getNameTypeAt ( i ) ;
if ( U_FAILURE ( status ) ) {
break ;
2011-05-03 14:59:01 +00:00
}
switch ( nameType ) {
case UTZNM_LONG_STANDARD :
2012-02-06 19:57:08 +00:00
// isLongStandard = TRUE;
2011-09-15 20:38:26 +00:00
case UTZNM_SHORT_STANDARD : // this one is never used for generic, but just in case
isStandard = TRUE ; // TODO: Remove this later, see the comments above.
2011-05-03 14:59:01 +00:00
bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD ;
break ;
case UTZNM_LONG_DAYLIGHT :
case UTZNM_SHORT_DAYLIGHT : // this one is never used for generic, but just in case
bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT ;
break ;
default :
bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN ;
}
}
}
delete tznamesMatches ;
2012-02-21 11:06:50 +00:00
if ( U_FAILURE ( status ) ) {
return 0 ;
}
2011-05-03 14:59:01 +00:00
if ( bestMatchLen = = ( text . length ( ) - start ) ) {
// Full match
//tzID.setTo(bestMatchTzID);
//timeType = bestMatchTimeType;
//return bestMatchLen;
// TODO Some time zone uses a same name for the long standard name
// and the location name. When the match is a long standard name,
// then we need to check if the name is same with the location name.
// This is probably a data error or a design bug.
2011-09-15 20:38:26 +00:00
/*
2011-05-03 14:59:01 +00:00
if ( ! isLongStandard ) {
tzID . setTo ( bestMatchTzID ) ;
timeType = bestMatchTimeType ;
return bestMatchLen ;
}
2011-09-15 20:38:26 +00:00
*/
// TODO The deprecation of commonlyUsed flag introduced the name
// conflict not only for long standard names, but short standard names too.
// These short names (found in zh_Hant) should be gone once we clean
// up CLDR time zone display name data. Once the short name conflict
// problem (with location name) is resolved, we should change the condition
// below back to the original one above. -Yoshito (2011-09-14)
if ( ! isStandard ) {
tzID . setTo ( bestMatchTzID ) ;
timeType = bestMatchTimeType ;
return bestMatchLen ;
}
2011-05-03 14:59:01 +00:00
}
}
// Find matches in the local trie
TimeZoneGenericNameMatchInfo * localMatches = findLocal ( text , start , types , status ) ;
if ( U_FAILURE ( status ) ) {
return 0 ;
}
if ( localMatches ! = NULL ) {
for ( int32_t i = 0 ; i < localMatches - > size ( ) ; i + + ) {
int32_t len = localMatches - > getMatchLength ( i ) ;
// TODO See the above TODO. We use len >= bestMatchLen
// because of the long standard/location name collision
// problem. If it is also a location name, carrying
// timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
// problem in SimpleDateFormat
if ( len > = bestMatchLen ) {
bestMatchLen = localMatches - > getMatchLength ( i ) ;
bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN ; // because generic
localMatches - > getTimeZoneID ( i , bestMatchTzID ) ;
}
}
delete localMatches ;
}
if ( bestMatchLen > 0 ) {
timeType = bestMatchTimeType ;
tzID . setTo ( bestMatchTzID ) ;
}
return bestMatchLen ;
}
TimeZoneGenericNameMatchInfo *
2012-02-21 11:06:50 +00:00
TZGNCore : : findLocal ( const UnicodeString & text , int32_t start , uint32_t types , UErrorCode & status ) const {
2011-05-03 14:59:01 +00:00
GNameSearchHandler handler ( types ) ;
2012-02-21 11:06:50 +00:00
TZGNCore * nonConstThis = const_cast < TZGNCore * > ( this ) ;
2011-05-03 14:59:01 +00:00
2012-10-05 21:22:02 +00:00
umtx_lock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
{
fGNamesTrie . search ( text , start , ( TextTrieMapSearchResultHandler * ) & handler , status ) ;
}
2012-10-05 21:22:02 +00:00
umtx_unlock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
if ( U_FAILURE ( status ) ) {
return NULL ;
}
TimeZoneGenericNameMatchInfo * gmatchInfo = NULL ;
int32_t maxLen = 0 ;
UVector * results = handler . getMatches ( maxLen ) ;
2011-07-01 21:07:32 +00:00
if ( results ! = NULL & & ( ( maxLen = = ( text . length ( ) - start ) ) | | fGNamesTrieFullyLoaded ) ) {
2011-05-03 14:59:01 +00:00
// perfect match
gmatchInfo = new TimeZoneGenericNameMatchInfo ( results ) ;
if ( gmatchInfo = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
delete results ;
return NULL ;
}
return gmatchInfo ;
}
if ( results ! = NULL ) {
delete results ;
}
// All names are not yet loaded into the local trie.
// Load all available names into the trie. This could be very heavy.
2012-10-05 21:22:02 +00:00
umtx_lock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
{
if ( ! fGNamesTrieFullyLoaded ) {
StringEnumeration * tzIDs = TimeZone : : createTimeZoneIDEnumeration ( UCAL_ZONE_TYPE_CANONICAL , NULL , NULL , status ) ;
if ( U_SUCCESS ( status ) ) {
const UnicodeString * tzID ;
while ( ( tzID = tzIDs - > snext ( status ) ) ) {
if ( U_FAILURE ( status ) ) {
break ;
}
nonConstThis - > loadStrings ( * tzID ) ;
}
}
if ( tzIDs ! = NULL ) {
delete tzIDs ;
}
if ( U_SUCCESS ( status ) ) {
nonConstThis - > fGNamesTrieFullyLoaded = TRUE ;
}
}
}
2012-10-05 21:22:02 +00:00
umtx_unlock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
if ( U_FAILURE ( status ) ) {
return NULL ;
}
2012-10-05 21:22:02 +00:00
umtx_lock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
{
// now try it again
fGNamesTrie . search ( text , start , ( TextTrieMapSearchResultHandler * ) & handler , status ) ;
}
2012-10-05 21:22:02 +00:00
umtx_unlock ( & gLock ) ;
2011-05-03 14:59:01 +00:00
results = handler . getMatches ( maxLen ) ;
if ( results ! = NULL & & maxLen > 0 ) {
gmatchInfo = new TimeZoneGenericNameMatchInfo ( results ) ;
if ( gmatchInfo = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
delete results ;
return NULL ;
}
}
return gmatchInfo ;
}
2012-02-21 11:06:50 +00:00
TimeZoneNames : : MatchInfoCollection *
TZGNCore : : findTimeZoneNames ( const UnicodeString & text , int32_t start , uint32_t types , UErrorCode & status ) const {
2011-05-03 14:59:01 +00:00
// Check if the target name typs is really in the TimeZoneNames
uint32_t nameTypes = 0 ;
if ( types & UTZGNM_LONG ) {
nameTypes | = ( UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD ) ;
}
if ( types & UTZGNM_SHORT ) {
2011-09-15 20:38:26 +00:00
nameTypes | = ( UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD ) ;
2011-05-03 14:59:01 +00:00
}
if ( types ) {
// Find matches in the TimeZoneNames
2012-02-21 11:06:50 +00:00
return fTimeZoneNames - > find ( text , start , nameTypes , status ) ;
}
return NULL ;
}
typedef struct TZGNCoreRef {
TZGNCore * obj ;
int32_t refCount ;
double lastAccess ;
} TZGNCoreRef ;
// TZGNCore object cache handling
2012-10-05 21:22:02 +00:00
static UMutex gTZGNLock = U_MUTEX_INITIALIZER ;
2012-02-21 11:06:50 +00:00
static UHashtable * gTZGNCoreCache = NULL ;
static UBool gTZGNCoreCacheInitialized = FALSE ;
// Access count - incremented every time up to SWEEP_INTERVAL,
// then reset to 0
static int32_t gAccessCount = 0 ;
// Interval for calling the cache sweep function - every 100 times
# define SWEEP_INTERVAL 100
// Cache expiration in millisecond. When a cached entry is no
// longer referenced and exceeding this threshold since last
// access time, then the cache entry will be deleted by the sweep
// function. For now, 3 minutes.
# define CACHE_EXPIRATION 180000.0
U_CDECL_BEGIN
/**
* Cleanup callback func
*/
static UBool U_CALLCONV tzgnCore_cleanup ( void )
{
if ( gTZGNCoreCache ! = NULL ) {
uhash_close ( gTZGNCoreCache ) ;
gTZGNCoreCache = NULL ;
}
gTZGNCoreCacheInitialized = FALSE ;
return TRUE ;
}
/**
* Deleter for TZGNCoreRef
*/
static void U_CALLCONV
deleteTZGNCoreRef ( void * obj ) {
icu : : TZGNCoreRef * entry = ( icu : : TZGNCoreRef * ) obj ;
delete ( icu : : TZGNCore * ) entry - > obj ;
uprv_free ( entry ) ;
}
U_CDECL_END
/**
* Function used for removing unreferrenced cache entries exceeding
* the expiration time . This function must be called with in the mutex
* block .
*/
static void sweepCache ( ) {
int32_t pos = - 1 ;
const UHashElement * elem ;
double now = ( double ) uprv_getUTCtime ( ) ;
while ( ( elem = uhash_nextElement ( gTZGNCoreCache , & pos ) ) ) {
TZGNCoreRef * entry = ( TZGNCoreRef * ) elem - > value . pointer ;
if ( entry - > refCount < = 0 & & ( now - entry - > lastAccess ) > CACHE_EXPIRATION ) {
// delete this entry
uhash_removeElement ( gTZGNCoreCache , elem ) ;
}
}
}
TimeZoneGenericNames : : TimeZoneGenericNames ( )
: fRef ( 0 ) {
}
TimeZoneGenericNames : : ~ TimeZoneGenericNames ( ) {
umtx_lock ( & gTZGNLock ) ;
{
U_ASSERT ( fRef - > refCount > 0 ) ;
// Just decrement the reference count
fRef - > refCount - - ;
}
umtx_unlock ( & gTZGNLock ) ;
}
TimeZoneGenericNames *
TimeZoneGenericNames : : createInstance ( const Locale & locale , UErrorCode & status ) {
if ( U_FAILURE ( status ) ) {
return NULL ;
}
TimeZoneGenericNames * instance = new TimeZoneGenericNames ( ) ;
if ( instance = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
return NULL ;
}
2013-05-31 23:50:15 +00:00
TZGNCoreRef * cacheEntry = NULL ;
{
Mutex lock ( & gTZGNLock ) ;
if ( ! gTZGNCoreCacheInitialized ) {
// Create empty hashtable
gTZGNCoreCache = uhash_open ( uhash_hashChars , uhash_compareChars , NULL , & status ) ;
if ( U_SUCCESS ( status ) ) {
uhash_setKeyDeleter ( gTZGNCoreCache , uprv_free ) ;
uhash_setValueDeleter ( gTZGNCoreCache , deleteTZGNCoreRef ) ;
gTZGNCoreCacheInitialized = TRUE ;
ucln_i18n_registerCleanup ( UCLN_I18N_TIMEZONEGENERICNAMES , tzgnCore_cleanup ) ;
2012-02-21 11:06:50 +00:00
}
}
if ( U_FAILURE ( status ) ) {
return NULL ;
}
2011-05-03 14:59:01 +00:00
2013-05-31 23:50:15 +00:00
// Check the cache, if not available, create new one and cache
2012-02-21 11:06:50 +00:00
const char * key = locale . getName ( ) ;
cacheEntry = ( TZGNCoreRef * ) uhash_get ( gTZGNCoreCache , key ) ;
if ( cacheEntry = = NULL ) {
TZGNCore * tzgnCore = NULL ;
char * newKey = NULL ;
tzgnCore = new TZGNCore ( locale , status ) ;
if ( tzgnCore = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
}
if ( U_SUCCESS ( status ) ) {
newKey = ( char * ) uprv_malloc ( uprv_strlen ( key ) + 1 ) ;
if ( newKey = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
} else {
uprv_strcpy ( newKey , key ) ;
}
}
if ( U_SUCCESS ( status ) ) {
cacheEntry = ( TZGNCoreRef * ) uprv_malloc ( sizeof ( TZGNCoreRef ) ) ;
if ( cacheEntry = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
} else {
cacheEntry - > obj = tzgnCore ;
cacheEntry - > refCount = 1 ;
cacheEntry - > lastAccess = ( double ) uprv_getUTCtime ( ) ;
uhash_put ( gTZGNCoreCache , newKey , cacheEntry , & status ) ;
}
}
if ( U_FAILURE ( status ) ) {
if ( tzgnCore ! = NULL ) {
delete tzgnCore ;
}
if ( newKey ! = NULL ) {
uprv_free ( newKey ) ;
}
if ( cacheEntry ! = NULL ) {
uprv_free ( cacheEntry ) ;
}
cacheEntry = NULL ;
}
} else {
// Update the reference count
cacheEntry - > refCount + + ;
cacheEntry - > lastAccess = ( double ) uprv_getUTCtime ( ) ;
}
gAccessCount + + ;
if ( gAccessCount > = SWEEP_INTERVAL ) {
// sweep
sweepCache ( ) ;
gAccessCount = 0 ;
}
2013-05-31 23:50:15 +00:00
} // End of mutex locked block
2012-02-21 11:06:50 +00:00
if ( cacheEntry = = NULL ) {
delete instance ;
return NULL ;
}
instance - > fRef = cacheEntry ;
return instance ;
}
UBool
TimeZoneGenericNames : : operator = = ( const TimeZoneGenericNames & other ) const {
// Just compare if the other object also use the same
// ref entry
return fRef = = other . fRef ;
}
TimeZoneGenericNames *
TimeZoneGenericNames : : clone ( ) const {
TimeZoneGenericNames * other = new TimeZoneGenericNames ( ) ;
if ( other ) {
umtx_lock ( & gTZGNLock ) ;
{
// Just increments the reference count
fRef - > refCount + + ;
other - > fRef = fRef ;
}
umtx_unlock ( & gTZGNLock ) ;
}
return other ;
}
UnicodeString &
TimeZoneGenericNames : : getDisplayName ( const TimeZone & tz , UTimeZoneGenericNameType type ,
UDate date , UnicodeString & name ) const {
return fRef - > obj - > getDisplayName ( tz , type , date , name ) ;
}
UnicodeString &
TimeZoneGenericNames : : getGenericLocationName ( const UnicodeString & tzCanonicalID , UnicodeString & name ) const {
return fRef - > obj - > getGenericLocationName ( tzCanonicalID , name ) ;
}
int32_t
TimeZoneGenericNames : : findBestMatch ( const UnicodeString & text , int32_t start , uint32_t types ,
UnicodeString & tzID , UTimeZoneFormatTimeType & timeType , UErrorCode & status ) const {
return fRef - > obj - > findBestMatch ( text , start , types , tzID , timeType , status ) ;
2011-05-03 14:59:01 +00:00
}
U_NAMESPACE_END
# endif