2 *******************************************************************************
3 * Copyright (C) 2001-2004, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 *******************************************************************************
9 #include "unicode/utypes.h"
11 #if !UCONFIG_NO_SERVICE
13 #include "unicode/resbund.h"
23 // see LocaleUtility::getAvailableLocaleNames
24 static Hashtable
* LocaleUtility_cache
= NULL
;
26 #define UNDERSCORE_CHAR ((UChar)0x005f)
27 #define AT_SIGN_CHAR ((UChar)64)
28 #define PERIOD_CHAR ((UChar)46)
31 ******************************************************************
35 * Release all static memory held by Locale Utility.
38 static UBool U_CALLCONV
service_cleanup(void) {
39 if (LocaleUtility_cache
) {
40 delete LocaleUtility_cache
;
41 LocaleUtility_cache
= NULL
;
50 LocaleUtility::canonicalLocaleString(const UnicodeString
* id
, UnicodeString
& result
)
55 // Fix case only (no other changes) up to the first '@' or '.' or
56 // end of string, whichever comes first. In 3.0 I changed this to
57 // stop at first '@' or '.'. It used to run out to the end of
58 // string. My fix makes the tests pass but is probably
59 // structurally incorrect. See below. [alan 3.0]
61 // TODO: Doug, you might want to revise this...
64 int32_t end
= result
.indexOf(AT_SIGN_CHAR
);
65 int32_t n
= result
.indexOf(PERIOD_CHAR
);
66 if (n
>= 0 && n
< end
) {
70 end
= result
.length();
72 n
= result
.indexOf(UNDERSCORE_CHAR
);
77 UChar c
= result
.charAt(i
);
78 if (c
>= 0x0041 && c
<= 0x005a) {
80 result
.setCharAt(i
, c
);
83 for (n
= end
; i
< n
; ++i
) {
84 UChar c
= result
.charAt(i
);
85 if (c
>= 0x0061 && c
<= 0x007a) {
87 result
.setCharAt(i
, c
);
94 // This code does a proper full level 2 canonicalization of id.
95 // It's nasty to go from UChar to char to char to UChar -- but
96 // that's what you have to do to use the uloc_canonicalize
97 // function on UnicodeStrings.
99 // I ended up doing the alternate fix (see above) not for
100 // performance reasons, although performance will certainly be
101 // better, but because doing a full level 2 canonicalization
102 // causes some tests to fail. [alan 3.0]
104 // TODO: Doug, you might want to revisit this...
107 int32_t buflen
= id
->length() + 8; // space for NUL
108 char* buf
= (char*) uprv_malloc(buflen
);
109 char* canon
= (buf
== 0) ? 0 : (char*) uprv_malloc(buflen
);
110 if (buf
!= 0 && canon
!= 0) {
111 U_ASSERT(id
->extract(0, INT32_MAX
, buf
, buflen
) < buflen
);
112 UErrorCode ec
= U_ZERO_ERROR
;
113 uloc_canonicalize(buf
, canon
, buflen
, &ec
);
115 result
= UnicodeString(canon
);
126 LocaleUtility::initLocaleFromName(const UnicodeString
& id
, Locale
& result
)
128 enum { BUFLEN
= 128 }; // larger than ever needed
130 if (id
.isBogus() || id
.length() >= BUFLEN
) {
134 * We need to convert from a UnicodeString to char * in order to
137 * Problem: Locale ID strings may contain '@' which is a variant
138 * character and cannot be handled by invariant-character conversion.
140 * Hack: Since ICU code can handle locale IDs with multiple encodings
141 * of '@' (at least for EBCDIC; it's not known to be a problem for
142 * ASCII-based systems),
143 * we use regular invariant-character conversion for everything else
144 * and manually convert U+0040 into a compiler-char-constant '@'.
145 * While this compilation-time constant may not match the runtime
146 * encoding of '@', it should be one of the encodings which ICU
149 * There should be only at most one '@' in a locale ID.
155 i
= id
.indexOf((UChar
)0x40, prev
);
157 // no @ between prev and the rest of the string
158 id
.extract(prev
, INT32_MAX
, buffer
+ prev
, BUFLEN
- prev
, US_INV
);
161 // normal invariant-character conversion for text between @s
162 id
.extract(prev
, i
- prev
, buffer
+ prev
, BUFLEN
- prev
, US_INV
);
163 // manually "convert" U+0040 at id[i] into '@' at buffer[i]
168 result
= Locale::createFromName(buffer
);
174 LocaleUtility::initNameFromLocale(const Locale
& locale
, UnicodeString
& result
)
176 if (locale
.isBogus()) {
179 result
.append(UnicodeString(locale
.getName(), -1, US_INV
));
185 LocaleUtility::getAvailableLocaleNames(const UnicodeString
& bundleID
)
187 // LocaleUtility_cache is a hash-of-hashes. The top-level keys
188 // are path strings ('bundleID') passed to
189 // ures_openAvailableLocales. The top-level values are
190 // second-level hashes. The second-level keys are result strings
191 // from ures_openAvailableLocales. The second-level values are
192 // garbage ((void*)1 or other random pointer).
194 UErrorCode status
= U_ZERO_ERROR
;
197 cache
= LocaleUtility_cache
;
201 cache
= new Hashtable(status
);
202 if (cache
== NULL
|| U_FAILURE(status
)) {
203 return NULL
; // catastrophic failure; e.g. out of memory
205 cache
->setValueDeleter(uhash_deleteHashtable
);
206 Hashtable
* h
; // set this to final LocaleUtility_cache value
208 h
= LocaleUtility_cache
;
210 LocaleUtility_cache
= h
= cache
;
212 ucln_common_registerCleanup(UCLN_COMMON_SERVICE
, service_cleanup
);
219 U_ASSERT(cache
!= NULL
);
223 htp
= (Hashtable
*) cache
->get(bundleID
);
227 htp
= new Hashtable(status
);
228 if (htp
&& U_SUCCESS(status
)) {
229 CharString
cbundleID(bundleID
);
230 const char* path
= (const char*) cbundleID
;
231 if (*path
== 0) path
= NULL
; // empty string => NULL
232 UEnumeration
*uenum
= ures_openAvailableLocales(path
, &status
);
234 const UChar
* id
= uenum_unext(uenum
, NULL
, &status
);
238 htp
->put(UnicodeString(id
), (void*)htp
, status
);
241 if (U_FAILURE(status
)) {
246 cache
->put(bundleID
, (void*)htp
, status
);
254 LocaleUtility::isFallbackOf(const UnicodeString
& root
, const UnicodeString
& child
)
256 return child
.indexOf(root
) == 0 &&
257 (child
.length() == root
.length() ||
258 child
.charAt(root
.length()) == UNDERSCORE_CHAR
);
262 ******************************************************************
265 const int32_t LocaleKey::KIND_ANY
= -1;
268 LocaleKey::createWithCanonicalFallback(const UnicodeString
* primaryID
,
269 const UnicodeString
* canonicalFallbackID
,
272 return LocaleKey::createWithCanonicalFallback(primaryID
, canonicalFallbackID
, KIND_ANY
, status
);
276 LocaleKey::createWithCanonicalFallback(const UnicodeString
* primaryID
,
277 const UnicodeString
* canonicalFallbackID
,
281 if (primaryID
== NULL
|| U_FAILURE(status
)) {
284 UnicodeString canonicalPrimaryID
;
285 LocaleUtility::canonicalLocaleString(primaryID
, canonicalPrimaryID
);
286 return new LocaleKey(*primaryID
, canonicalPrimaryID
, canonicalFallbackID
, kind
);
289 LocaleKey::LocaleKey(const UnicodeString
& primaryID
,
290 const UnicodeString
& canonicalPrimaryID
,
291 const UnicodeString
* canonicalFallbackID
,
293 : ICUServiceKey(primaryID
)
295 , _primaryID(canonicalPrimaryID
)
299 _fallbackID
.setToBogus();
300 if (_primaryID
.length() != 0) {
301 if (canonicalFallbackID
!= NULL
&& _primaryID
!= *canonicalFallbackID
) {
302 _fallbackID
= *canonicalFallbackID
;
306 _currentID
= _primaryID
;
309 LocaleKey::~LocaleKey() {}
312 LocaleKey::prefix(UnicodeString
& result
) const {
313 if (_kind
!= KIND_ANY
) {
315 uprv_itou(buffer
, 64, _kind
, 10, 0);
316 UnicodeString
temp(buffer
);
323 LocaleKey::kind() const {
328 LocaleKey::canonicalID(UnicodeString
& result
) const {
329 return result
.append(_primaryID
);
333 LocaleKey::currentID(UnicodeString
& result
) const {
334 if (!_currentID
.isBogus()) {
335 result
.append(_currentID
);
341 LocaleKey::currentDescriptor(UnicodeString
& result
) const {
342 if (!_currentID
.isBogus()) {
343 prefix(result
).append(PREFIX_DELIMITER
).append(_currentID
);
351 LocaleKey::canonicalLocale(Locale
& result
) const {
352 return LocaleUtility::initLocaleFromName(_primaryID
, result
);
356 LocaleKey::currentLocale(Locale
& result
) const {
357 return LocaleUtility::initLocaleFromName(_currentID
, result
);
361 LocaleKey::fallback() {
362 if (!_currentID
.isBogus()) {
363 int x
= _currentID
.lastIndexOf(UNDERSCORE_CHAR
);
365 _currentID
.remove(x
); // truncate current or fallback, whichever we're pointing to
369 if (!_fallbackID
.isBogus()) {
370 _currentID
= _fallbackID
;
371 _fallbackID
.setToBogus();
375 if (_currentID
.length() > 0) {
376 _currentID
.remove(0); // completely truncate
380 _currentID
.setToBogus();
387 LocaleKey::isFallbackOf(const UnicodeString
& id
) const {
388 UnicodeString
temp(id
);
390 return temp
.indexOf(_primaryID
) == 0 &&
391 (temp
.length() == _primaryID
.length() ||
392 temp
.charAt(_primaryID
.length()) == UNDERSCORE_CHAR
);
397 LocaleKey::debug(UnicodeString
& result
) const
399 ICUServiceKey::debug(result
);
400 result
.append(" kind: ");
401 result
.append(_kind
);
402 result
.append(" primaryID: ");
403 result
.append(_primaryID
);
404 result
.append(" fallbackID: ");
405 result
.append(_fallbackID
);
406 result
.append(" currentID: ");
407 result
.append(_currentID
);
412 LocaleKey::debugClass(UnicodeString
& result
) const
414 return result
.append("LocaleKey ");
418 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKey
)
421 ******************************************************************
424 LocaleKeyFactory::LocaleKeyFactory(int32_t coverage
)
426 , _coverage(coverage
)
430 LocaleKeyFactory::LocaleKeyFactory(int32_t coverage
, const UnicodeString
& name
)
432 , _coverage(coverage
)
436 LocaleKeyFactory::~LocaleKeyFactory() {
440 LocaleKeyFactory::create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const {
441 if (handlesKey(key
, status
)) {
442 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
443 int32_t kind
= lkey
.kind();
445 lkey
.currentLocale(loc
);
447 return handleCreate(loc
, kind
, service
, status
);
453 LocaleKeyFactory::handlesKey(const ICUServiceKey
& key
, UErrorCode
& status
) const {
454 const Hashtable
* supported
= getSupportedIDs(status
);
458 return supported
->get(id
) != NULL
;
464 LocaleKeyFactory::updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const {
465 const Hashtable
* supported
= getSupportedIDs(status
);
467 UBool visible
= (_coverage
& 0x1) == 0;
469 const UHashElement
* elem
= NULL
;
471 while ((elem
= supported
->nextElement(pos
)) != NULL
) {
472 const UnicodeString
& id
= *((const UnicodeString
*)elem
->key
.pointer
);
476 result
.put(id
, (void*)this, status
); // this is dummy non-void marker used for set semantics
477 if (U_FAILURE(status
)) {
486 LocaleKeyFactory::getDisplayName(const UnicodeString
& id
, const Locale
& locale
, UnicodeString
& result
) const {
487 if ((_coverage
& 0x1) == 0) {
488 //UErrorCode status = U_ZERO_ERROR;
489 // assume if this is called on us, we support some fallback of this id
490 // if (isSupportedID(id, status)) {
492 LocaleUtility::initLocaleFromName(id
, loc
);
493 return loc
.getDisplayName(locale
, result
);
501 LocaleKeyFactory::handleCreate(const Locale
& /* loc */,
503 const ICUService
* /* service */,
504 UErrorCode
& /* status */) const {
509 LocaleKeyFactory::isSupportedID(const UnicodeString
& id
, UErrorCode
& status
) const {
510 const Hashtable
* ids
= getSupportedIDs(status
);
511 return ids
&& ids
->get(id
);
515 LocaleKeyFactory::getSupportedIDs(UErrorCode
& /* status */) const {
521 LocaleKeyFactory::debug(UnicodeString
& result
) const
524 result
.append(", name: ");
525 result
.append(_name
);
526 result
.append(", coverage: ");
527 result
.append(_coverage
);
532 LocaleKeyFactory::debugClass(UnicodeString
& result
) const
534 return result
.append("LocaleKeyFactory");
538 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKeyFactory
)
541 ******************************************************************
544 SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject
* objToAdopt
,
545 const UnicodeString
& locale
,
548 : LocaleKeyFactory(coverage
)
555 SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject
* objToAdopt
,
556 const Locale
& locale
,
559 : LocaleKeyFactory(coverage
)
564 LocaleUtility::initNameFromLocale(locale
, _id
);
567 SimpleLocaleKeyFactory::~SimpleLocaleKeyFactory()
574 SimpleLocaleKeyFactory::create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const
576 if (U_SUCCESS(status
)) {
577 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
578 if (_kind
== LocaleKey::KIND_ANY
|| _kind
== lkey
.kind()) {
580 lkey
.currentID(keyID
);
582 return service
->cloneInstance(_obj
);
590 SimpleLocaleKeyFactory::isSupportedID(const UnicodeString
& id
, UErrorCode
& /* status */) const
596 SimpleLocaleKeyFactory::updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const
598 if (U_SUCCESS(status
)) {
599 if (_coverage
& 0x1) {
602 result
.put(_id
, (void*)this, status
);
609 SimpleLocaleKeyFactory::debug(UnicodeString
& result
) const
611 LocaleKeyFactory::debug(result
);
612 result
.append(", id: ");
614 result
.append(", kind: ");
615 result
.append(_kind
);
620 SimpleLocaleKeyFactory::debugClass(UnicodeString
& result
) const
622 return result
.append("SimpleLocaleKeyFactory");
626 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleLocaleKeyFactory
)
629 ******************************************************************
632 ICUResourceBundleFactory::ICUResourceBundleFactory()
633 : LocaleKeyFactory(VISIBLE
)
638 ICUResourceBundleFactory::ICUResourceBundleFactory(const UnicodeString
& bundleName
)
639 : LocaleKeyFactory(VISIBLE
)
640 , _bundleName(bundleName
)
644 ICUResourceBundleFactory::~ICUResourceBundleFactory() {}
647 ICUResourceBundleFactory::getSupportedIDs(UErrorCode
& status
) const
649 if (U_SUCCESS(status
)) {
650 return LocaleUtility::getAvailableLocaleNames(_bundleName
);
656 ICUResourceBundleFactory::handleCreate(const Locale
& loc
, int32_t /* kind */, const ICUService
* /* service */, UErrorCode
& status
) const
658 if (U_SUCCESS(status
)) {
659 // _bundleName is a package name
660 // and should only contain invariant characters
663 length
=_bundleName
.extract(0, INT32_MAX
, pkg
, (int32_t)sizeof(pkg
), US_INV
);
664 if(length
>=(int32_t)sizeof(pkg
)) {
667 return new ResourceBundle(pkg
, loc
, status
);
674 ICUResourceBundleFactory::debug(UnicodeString
& result
) const
676 LocaleKeyFactory::debug(result
);
677 result
.append(", bundle: ");
678 return result
.append(_bundleName
);
682 ICUResourceBundleFactory::debugClass(UnicodeString
& result
) const
684 return result
.append("ICUResourceBundleFactory");
688 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUResourceBundleFactory
)
691 ******************************************************************
694 ICULocaleService::ICULocaleService()
695 : fallbackLocale(Locale::getDefault())
701 ICULocaleService::ICULocaleService(const UnicodeString
& dname
)
703 , fallbackLocale(Locale::getDefault())
709 ICULocaleService::~ICULocaleService()
711 umtx_destroy(&llock
);
715 ICULocaleService::get(const Locale
& locale
, UErrorCode
& status
) const
717 return get(locale
, LocaleKey::KIND_ANY
, NULL
, status
);
721 ICULocaleService::get(const Locale
& locale
, int32_t kind
, UErrorCode
& status
) const
723 return get(locale
, kind
, NULL
, status
);
727 ICULocaleService::get(const Locale
& locale
, Locale
* actualReturn
, UErrorCode
& status
) const
729 return get(locale
, LocaleKey::KIND_ANY
, actualReturn
, status
);
733 ICULocaleService::get(const Locale
& locale
, int32_t kind
, Locale
* actualReturn
, UErrorCode
& status
) const
735 UObject
* result
= NULL
;
736 if (U_FAILURE(status
)) {
740 UnicodeString
locName(locale
.getName(), -1, US_INV
);
741 if (locName
.isBogus()) {
742 status
= U_MEMORY_ALLOCATION_ERROR
;
744 ICUServiceKey
* key
= createKey(&locName
, kind
, status
);
746 if (actualReturn
== NULL
) {
747 result
= getKey(*key
, status
);
750 result
= getKey(*key
, &temp
, status
);
752 if (result
!= NULL
) {
753 key
->parseSuffix(temp
);
754 LocaleUtility::initLocaleFromName(temp
, *actualReturn
);
765 ICULocaleService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& locale
,
766 UBool visible
, UErrorCode
& status
)
769 LocaleUtility::initLocaleFromName(locale
, loc
);
770 return registerInstance(objToAdopt
, loc
, LocaleKey::KIND_ANY
,
771 visible
? LocaleKeyFactory::VISIBLE
: LocaleKeyFactory::INVISIBLE
, status
);
775 ICULocaleService::registerInstance(UObject
* objToAdopt
, const Locale
& locale
, UErrorCode
& status
)
777 return registerInstance(objToAdopt
, locale
, LocaleKey::KIND_ANY
, LocaleKeyFactory::VISIBLE
, status
);
781 ICULocaleService::registerInstance(UObject
* objToAdopt
, const Locale
& locale
, int32_t kind
, UErrorCode
& status
)
783 return registerInstance(objToAdopt
, locale
, kind
, LocaleKeyFactory::VISIBLE
, status
);
787 ICULocaleService::registerInstance(UObject
* objToAdopt
, const Locale
& locale
, int32_t kind
, int32_t coverage
, UErrorCode
& status
)
789 ICUServiceFactory
* factory
= new SimpleLocaleKeyFactory(objToAdopt
, locale
, kind
, coverage
);
790 if (factory
!= NULL
) {
791 return registerFactory(factory
, status
);
799 ICULocaleService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& locale
, UErrorCode
& status
)
801 return registerInstance(objToAdopt
, locale
, LocaleKey::KIND_ANY
, LocaleKeyFactory::VISIBLE
, status
);
805 ICULocaleService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& locale
, UBool visible
, UErrorCode
& status
)
807 return registerInstance(objToAdopt
, locale
, LocaleKey::KIND_ANY
,
808 visible
? LocaleKeyFactory::VISIBLE
: LocaleKeyFactory::INVISIBLE
,
813 ICULocaleService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& locale
, int32_t kind
, int32_t coverage
, UErrorCode
& status
)
815 ICUServiceFactory
* factory
= new SimpleLocaleKeyFactory(objToAdopt
, locale
, kind
, coverage
);
816 if (factory
!= NULL
) {
817 return registerFactory(factory
, status
);
824 class ServiceEnumeration
: public StringEnumeration
{
826 const ICULocaleService
* _service
;
832 ServiceEnumeration(const ICULocaleService
* service
, UErrorCode
&status
)
834 , _timestamp(service
->getTimestamp())
835 , _ids(uhash_deleteUnicodeString
, NULL
, status
)
838 _service
->getVisibleIDs(_ids
, status
);
841 ServiceEnumeration(const ServiceEnumeration
&other
, UErrorCode
&status
)
842 : _service(other
._service
)
843 , _timestamp(other
._timestamp
)
844 , _ids(uhash_deleteUnicodeString
, NULL
, status
)
847 if(U_SUCCESS(status
)) {
850 length
= other
._ids
.size();
851 for(i
= 0; i
< length
; ++i
) {
852 _ids
.addElement(((UnicodeString
*)other
._ids
.elementAt(i
))->clone(), status
);
855 if(U_SUCCESS(status
)) {
862 static ServiceEnumeration
* create(const ICULocaleService
* service
) {
863 UErrorCode status
= U_ZERO_ERROR
;
864 ServiceEnumeration
* result
= new ServiceEnumeration(service
, status
);
865 if (U_SUCCESS(status
)) {
872 virtual ~ServiceEnumeration() {}
874 virtual StringEnumeration
*clone() const {
875 UErrorCode status
= U_ZERO_ERROR
;
876 ServiceEnumeration
*cl
= new ServiceEnumeration(*this, status
);
877 if(U_FAILURE(status
)) {
884 UBool
upToDate(UErrorCode
& status
) const {
885 if (U_SUCCESS(status
)) {
886 if (_timestamp
== _service
->getTimestamp()) {
889 status
= U_ENUM_OUT_OF_SYNC_ERROR
;
894 virtual int32_t count(UErrorCode
& status
) const {
895 return upToDate(status
) ? _ids
.size() : 0;
898 virtual const UnicodeString
* snext(UErrorCode
& status
) {
899 if (upToDate(status
) && (_pos
< _ids
.size())) {
900 return (const UnicodeString
*)_ids
[_pos
++];
905 virtual void reset(UErrorCode
& status
) {
906 if (status
== U_ENUM_OUT_OF_SYNC_ERROR
) {
907 status
= U_ZERO_ERROR
;
909 if (U_SUCCESS(status
)) {
910 _timestamp
= _service
->getTimestamp();
912 _service
->getVisibleIDs(_ids
, status
);
917 static UClassID U_EXPORT2
getStaticClassID(void);
918 virtual UClassID
getDynamicClassID(void) const;
921 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceEnumeration
)
924 ICULocaleService::getAvailableLocales(void) const
926 return ServiceEnumeration::create(this);
930 ICULocaleService::validateFallbackLocale() const
932 const Locale
& loc
= Locale::getDefault();
933 ICULocaleService
* ncThis
= (ICULocaleService
*)this;
935 Mutex
mutex(&ncThis
->llock
);
936 if (loc
!= fallbackLocale
) {
937 ncThis
->fallbackLocale
= loc
;
938 LocaleUtility::initNameFromLocale(loc
, ncThis
->fallbackLocaleName
);
939 ncThis
->clearServiceCache();
942 return fallbackLocaleName
;
946 ICULocaleService::createKey(const UnicodeString
* id
, UErrorCode
& status
) const
948 return LocaleKey::createWithCanonicalFallback(id
, &validateFallbackLocale(), status
);
952 ICULocaleService::createKey(const UnicodeString
* id
, int32_t kind
, UErrorCode
& status
) const
954 return LocaleKey::createWithCanonicalFallback(id
, &validateFallbackLocale(), kind
, status
);
959 /* !UCONFIG_NO_SERVICE */