1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2001-2014, International Business Machines Corporation.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_SERVICE
17 #undef SERVICE_REFCOUNT
19 // in case we use the refcount stuff
24 ******************************************************************
27 const UChar
ICUServiceKey::PREFIX_DELIMITER
= 0x002F; /* '/' */
29 ICUServiceKey::ICUServiceKey(const UnicodeString
& id
)
33 ICUServiceKey::~ICUServiceKey()
38 ICUServiceKey::getID() const
44 ICUServiceKey::canonicalID(UnicodeString
& result
) const
46 return result
.append(_id
);
50 ICUServiceKey::currentID(UnicodeString
& result
) const
52 return canonicalID(result
);
56 ICUServiceKey::currentDescriptor(UnicodeString
& result
) const
59 result
.append(PREFIX_DELIMITER
);
60 return currentID(result
);
64 ICUServiceKey::fallback()
70 ICUServiceKey::isFallbackOf(const UnicodeString
& id
) const
76 ICUServiceKey::prefix(UnicodeString
& result
) const
82 ICUServiceKey::parsePrefix(UnicodeString
& result
)
84 int32_t n
= result
.indexOf(PREFIX_DELIMITER
);
93 ICUServiceKey::parseSuffix(UnicodeString
& result
)
95 int32_t n
= result
.indexOf(PREFIX_DELIMITER
);
97 result
.remove(0, n
+1);
104 ICUServiceKey::debug(UnicodeString
& result
) const
107 result
.append((UnicodeString
)" id: ");
113 ICUServiceKey::debugClass(UnicodeString
& result
) const
115 return result
.append((UnicodeString
)"ICUServiceKey");
119 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey
)
122 ******************************************************************
125 ICUServiceFactory::~ICUServiceFactory() {}
127 SimpleFactory::SimpleFactory(UObject
* instanceToAdopt
, const UnicodeString
& id
, UBool visible
)
128 : _instance(instanceToAdopt
), _id(id
), _visible(visible
)
132 SimpleFactory::~SimpleFactory()
138 SimpleFactory::create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const
140 if (U_SUCCESS(status
)) {
142 if (_id
== key
.currentID(temp
)) {
143 return service
->cloneInstance(_instance
);
150 SimpleFactory::updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const
153 result
.put(_id
, (void*)this, status
); // cast away const
160 SimpleFactory::getDisplayName(const UnicodeString
& id
, const Locale
& /* locale */, UnicodeString
& result
) const
162 if (_visible
&& _id
== id
) {
172 SimpleFactory::debug(UnicodeString
& toAppendTo
) const
174 debugClass(toAppendTo
);
175 toAppendTo
.append((UnicodeString
)" id: ");
176 toAppendTo
.append(_id
);
177 toAppendTo
.append((UnicodeString
)", visible: ");
178 toAppendTo
.append(_visible
? (UnicodeString
)"T" : (UnicodeString
)"F");
183 SimpleFactory::debugClass(UnicodeString
& toAppendTo
) const
185 return toAppendTo
.append((UnicodeString
)"SimpleFactory");
189 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory
)
192 ******************************************************************
195 ServiceListener::~ServiceListener() {}
197 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener
)
200 ******************************************************************
203 // Record the actual id for this service in the cache, so we can return it
204 // even if we succeed later with a different id.
205 class CacheEntry
: public UMemory
{
210 UnicodeString actualDescriptor
;
214 * Releases a reference to the shared resource.
220 CacheEntry(const UnicodeString
& _actualDescriptor
, UObject
* _service
)
221 : refcount(1), actualDescriptor(_actualDescriptor
), service(_service
) {
225 * Instantiation creates an initial reference, so don't call this
226 * unless you're creating a new pointer to this. Management of
227 * that pointer will have to know how to deal with refcounts.
228 * Return true if the resource has not already been released.
236 * Destructions removes a reference, so don't call this unless
237 * you're removing pointer to this somewhere. Management of that
238 * pointer will have to know how to deal with refcounts. Once
239 * the refcount drops to zero, the resource is released. Return
240 * false if the resouce has been released.
242 CacheEntry
* unref() {
243 if ((--refcount
) == 0) {
251 * Return TRUE if there is at least one reference to this and the
252 * resource has not been released.
254 UBool
isShared() const {
259 // UObjectDeleter for serviceCache
261 static void U_CALLCONV
262 cacheDeleter(void* obj
) {
263 U_NAMESPACE_USE ((CacheEntry
*)obj
)->unref();
267 * Deleter for UObjects
269 static void U_CALLCONV
270 deleteUObject(void *obj
) {
271 U_NAMESPACE_USE
delete (UObject
*) obj
;
276 ******************************************************************
279 class DNCache
: public UMemory
{
284 DNCache(const Locale
& _locale
)
285 : cache(), locale(_locale
)
287 // cache.setKeyDeleter(uprv_deleteUObject);
293 ******************************************************************
297 StringPair::create(const UnicodeString
& displayName
,
298 const UnicodeString
& id
,
301 if (U_SUCCESS(status
)) {
302 StringPair
* sp
= new StringPair(displayName
, id
);
303 if (sp
== NULL
|| sp
->isBogus()) {
304 status
= U_MEMORY_ALLOCATION_ERROR
;
314 StringPair::isBogus() const {
315 return displayName
.isBogus() || id
.isBogus();
318 StringPair::StringPair(const UnicodeString
& _displayName
,
319 const UnicodeString
& _id
)
320 : displayName(_displayName
)
326 static void U_CALLCONV
327 userv_deleteStringPair(void *obj
) {
328 U_NAMESPACE_USE
delete (StringPair
*) obj
;
333 ******************************************************************
336 static UMutex
*lock() {
337 static UMutex
*m
= STATIC_NEW(UMutex
);
341 ICUService::ICUService()
351 ICUService::ICUService(const UnicodeString
& newName
)
361 ICUService::~ICUService()
372 ICUService::get(const UnicodeString
& descriptor
, UErrorCode
& status
) const
374 return get(descriptor
, NULL
, status
);
378 ICUService::get(const UnicodeString
& descriptor
, UnicodeString
* actualReturn
, UErrorCode
& status
) const
380 UObject
* result
= NULL
;
381 ICUServiceKey
* key
= createKey(&descriptor
, status
);
383 result
= getKey(*key
, actualReturn
, status
);
390 ICUService::getKey(ICUServiceKey
& key
, UErrorCode
& status
) const
392 return getKey(key
, NULL
, status
);
395 // this is a vector that subclasses of ICUService can override to further customize the result object
396 // before returning it. All other public get functions should call this one.
399 ICUService::getKey(ICUServiceKey
& key
, UnicodeString
* actualReturn
, UErrorCode
& status
) const
401 return getKey(key
, actualReturn
, NULL
, status
);
404 // make it possible to call reentrantly on systems that don't have reentrant mutexes.
405 // we can use this simple approach since we know the situation where we're calling
406 // reentrantly even without knowing the thread.
407 class XMutex
: public UMemory
{
409 inline XMutex(UMutex
*mutex
, UBool reentering
)
411 , fActive(!reentering
)
413 if (fActive
) umtx_lock(fMutex
);
416 if (fActive
) umtx_unlock(fMutex
);
424 struct UVectorDeleter
{
426 UVectorDeleter() : _obj(NULL
) {}
427 ~UVectorDeleter() { delete _obj
; }
430 // called only by factories, treat as private
432 ICUService::getKey(ICUServiceKey
& key
, UnicodeString
* actualReturn
, const ICUServiceFactory
* factory
, UErrorCode
& status
) const
434 if (U_FAILURE(status
)) {
439 return handleDefault(key
, actualReturn
, status
);
442 ICUService
* ncthis
= (ICUService
*)this; // cast away semantic const
444 CacheEntry
* result
= NULL
;
446 // The factory list can't be modified until we're done,
447 // otherwise we might update the cache with an invalid result.
448 // The cache has to stay in synch with the factory list.
449 // ICU doesn't have monitors so we can't use rw locks, so
450 // we single-thread everything using this service, for now.
452 // if factory is not null, we're calling from within the mutex,
453 // and since some unix machines don't have reentrant mutexes we
454 // need to make sure not to try to lock it again.
455 XMutex
mutex(lock(), factory
!= NULL
);
457 if (serviceCache
== NULL
) {
458 ncthis
->serviceCache
= new Hashtable(status
);
459 if (ncthis
->serviceCache
== NULL
) {
462 if (U_FAILURE(status
)) {
466 serviceCache
->setValueDeleter(cacheDeleter
);
469 UnicodeString currentDescriptor
;
470 UVectorDeleter cacheDescriptorList
;
471 UBool putInCache
= FALSE
;
473 int32_t startIndex
= 0;
474 int32_t limit
= factories
->size();
475 UBool cacheResult
= TRUE
;
477 if (factory
!= NULL
) {
478 for (int32_t i
= 0; i
< limit
; ++i
) {
479 if (factory
== (const ICUServiceFactory
*)factories
->elementAt(i
)) {
484 if (startIndex
== 0) {
485 // throw new InternalError("Factory " + factory + "not registered with service: " + this);
486 status
= U_ILLEGAL_ARGUMENT_ERROR
;
493 currentDescriptor
.remove();
494 key
.currentDescriptor(currentDescriptor
);
495 result
= (CacheEntry
*)serviceCache
->get(currentDescriptor
);
496 if (result
!= NULL
) {
500 // first test of cache failed, so we'll have to update
501 // the cache if we eventually succeed-- that is, if we're
502 // going to update the cache at all.
505 int32_t index
= startIndex
;
506 while (index
< limit
) {
507 ICUServiceFactory
* f
= (ICUServiceFactory
*)factories
->elementAt(index
++);
508 UObject
* service
= f
->create(key
, this, status
);
509 if (U_FAILURE(status
)) {
513 if (service
!= NULL
) {
514 result
= new CacheEntry(currentDescriptor
, service
);
515 if (result
== NULL
) {
517 status
= U_MEMORY_ALLOCATION_ERROR
;
525 // prepare to load the cache with all additional ids that
526 // will resolve to result, assuming we'll succeed. We
527 // don't want to keep querying on an id that's going to
528 // fallback to the one that succeeded, we want to hit the
529 // cache the first time next goaround.
530 if (cacheDescriptorList
._obj
== NULL
) {
531 cacheDescriptorList
._obj
= new UVector(uprv_deleteUObject
, NULL
, 5, status
);
532 if (U_FAILURE(status
)) {
536 UnicodeString
* idToCache
= new UnicodeString(currentDescriptor
);
537 if (idToCache
== NULL
|| idToCache
->isBogus()) {
538 status
= U_MEMORY_ALLOCATION_ERROR
;
542 cacheDescriptorList
._obj
->addElement(idToCache
, status
);
543 if (U_FAILURE(status
)) {
546 } while (key
.fallback());
549 if (result
!= NULL
) {
550 if (putInCache
&& cacheResult
) {
551 serviceCache
->put(result
->actualDescriptor
, result
, status
);
552 if (U_FAILURE(status
)) {
556 if (cacheDescriptorList
._obj
!= NULL
) {
557 for (int32_t i
= cacheDescriptorList
._obj
->size(); --i
>= 0;) {
558 UnicodeString
* desc
= (UnicodeString
*)cacheDescriptorList
._obj
->elementAt(i
);
560 serviceCache
->put(*desc
, result
, status
);
561 if (U_FAILURE(status
)) {
566 cacheDescriptorList
._obj
->removeElementAt(i
);
571 if (actualReturn
!= NULL
) {
573 if (result
->actualDescriptor
.indexOf((UChar
)0x2f) == 0) { // U+002f=slash (/)
574 actualReturn
->remove();
575 actualReturn
->append(result
->actualDescriptor
,
577 result
->actualDescriptor
.length() - 1);
579 *actualReturn
= result
->actualDescriptor
;
582 if (actualReturn
->isBogus()) {
583 status
= U_MEMORY_ALLOCATION_ERROR
;
589 UObject
* service
= cloneInstance(result
->service
);
590 if (putInCache
&& !cacheResult
) {
597 return handleDefault(key
, actualReturn
, status
);
601 ICUService::handleDefault(const ICUServiceKey
& /* key */, UnicodeString
* /* actualIDReturn */, UErrorCode
& /* status */) const
607 ICUService::getVisibleIDs(UVector
& result
, UErrorCode
& status
) const {
608 return getVisibleIDs(result
, NULL
, status
);
612 ICUService::getVisibleIDs(UVector
& result
, const UnicodeString
* matchID
, UErrorCode
& status
) const
614 result
.removeAllElements();
616 if (U_FAILURE(status
)) {
622 const Hashtable
* map
= getVisibleIDMap(status
);
624 ICUServiceKey
* fallbackKey
= createKey(matchID
, status
);
626 for (int32_t pos
= UHASH_FIRST
;;) {
627 const UHashElement
* e
= map
->nextElement(pos
);
632 const UnicodeString
* id
= (const UnicodeString
*)e
->key
.pointer
;
633 if (fallbackKey
!= NULL
) {
634 if (!fallbackKey
->isFallbackOf(*id
)) {
639 UnicodeString
* idClone
= new UnicodeString(*id
);
640 if (idClone
== NULL
|| idClone
->isBogus()) {
642 status
= U_MEMORY_ALLOCATION_ERROR
;
645 result
.addElement(idClone
, status
);
646 if (U_FAILURE(status
)) {
654 if (U_FAILURE(status
)) {
655 result
.removeAllElements();
661 ICUService::getVisibleIDMap(UErrorCode
& status
) const {
662 if (U_FAILURE(status
)) return NULL
;
664 // must only be called when lock is already held
666 ICUService
* ncthis
= (ICUService
*)this; // cast away semantic const
667 if (idCache
== NULL
) {
668 ncthis
->idCache
= new Hashtable(status
);
669 if (idCache
== NULL
) {
670 status
= U_MEMORY_ALLOCATION_ERROR
;
671 } else if (factories
!= NULL
) {
672 for (int32_t pos
= factories
->size(); --pos
>= 0;) {
673 ICUServiceFactory
* f
= (ICUServiceFactory
*)factories
->elementAt(pos
);
674 f
->updateVisibleIDs(*idCache
, status
);
676 if (U_FAILURE(status
)) {
678 ncthis
->idCache
= NULL
;
688 ICUService::getDisplayName(const UnicodeString
& id
, UnicodeString
& result
) const
690 return getDisplayName(id
, result
, Locale::getDefault());
694 ICUService::getDisplayName(const UnicodeString
& id
, UnicodeString
& result
, const Locale
& locale
) const
697 UErrorCode status
= U_ZERO_ERROR
;
699 const Hashtable
* map
= getVisibleIDMap(status
);
701 ICUServiceFactory
* f
= (ICUServiceFactory
*)map
->get(id
);
703 f
->getDisplayName(id
, locale
, result
);
708 status
= U_ZERO_ERROR
;
709 ICUServiceKey
* fallbackKey
= createKey(&id
, status
);
710 while (fallbackKey
!= NULL
&& fallbackKey
->fallback()) {
712 fallbackKey
->currentID(us
);
713 f
= (ICUServiceFactory
*)map
->get(us
);
715 f
->getDisplayName(id
, locale
, result
);
728 ICUService::getDisplayNames(UVector
& result
, UErrorCode
& status
) const
730 return getDisplayNames(result
, Locale::getDefault(), NULL
, status
);
735 ICUService::getDisplayNames(UVector
& result
, const Locale
& locale
, UErrorCode
& status
) const
737 return getDisplayNames(result
, locale
, NULL
, status
);
741 ICUService::getDisplayNames(UVector
& result
,
742 const Locale
& locale
,
743 const UnicodeString
* matchID
,
744 UErrorCode
& status
) const
746 result
.removeAllElements();
747 result
.setDeleter(userv_deleteStringPair
);
748 if (U_SUCCESS(status
)) {
749 ICUService
* ncthis
= (ICUService
*)this; // cast away semantic const
752 if (dnCache
!= NULL
&& dnCache
->locale
!= locale
) {
754 ncthis
->dnCache
= NULL
;
757 if (dnCache
== NULL
) {
758 const Hashtable
* m
= getVisibleIDMap(status
);
759 if (U_FAILURE(status
)) {
762 ncthis
->dnCache
= new DNCache(locale
);
763 if (dnCache
== NULL
) {
764 status
= U_MEMORY_ALLOCATION_ERROR
;
768 int32_t pos
= UHASH_FIRST
;
769 const UHashElement
* entry
= NULL
;
770 while ((entry
= m
->nextElement(pos
)) != NULL
) {
771 const UnicodeString
* id
= (const UnicodeString
*)entry
->key
.pointer
;
772 ICUServiceFactory
* f
= (ICUServiceFactory
*)entry
->value
.pointer
;
774 f
->getDisplayName(*id
, locale
, dname
);
775 if (dname
.isBogus()) {
776 status
= U_MEMORY_ALLOCATION_ERROR
;
778 dnCache
->cache
.put(dname
, (void*)id
, status
); // share pointer with visibleIDMap
779 if (U_SUCCESS(status
)) {
784 ncthis
->dnCache
= NULL
;
790 ICUServiceKey
* matchKey
= createKey(matchID
, status
);
791 /* To ensure that all elements in the hashtable are iterated, set pos to -1.
792 * nextElement(pos) will skip the position at pos and begin the iteration
793 * at the next position, which in this case will be 0.
795 int32_t pos
= UHASH_FIRST
;
796 const UHashElement
*entry
= NULL
;
797 while ((entry
= dnCache
->cache
.nextElement(pos
)) != NULL
) {
798 const UnicodeString
* id
= (const UnicodeString
*)entry
->value
.pointer
;
799 if (matchKey
!= NULL
&& !matchKey
->isFallbackOf(*id
)) {
802 const UnicodeString
* dn
= (const UnicodeString
*)entry
->key
.pointer
;
803 StringPair
* sp
= StringPair::create(*id
, *dn
, status
);
804 result
.addElement(sp
, status
);
805 if (U_FAILURE(status
)) {
806 result
.removeAllElements();
816 ICUService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& id
, UErrorCode
& status
)
818 return registerInstance(objToAdopt
, id
, TRUE
, status
);
822 ICUService::registerInstance(UObject
* objToAdopt
, const UnicodeString
& id
, UBool visible
, UErrorCode
& status
)
824 ICUServiceKey
* key
= createKey(&id
, status
);
826 UnicodeString canonicalID
;
827 key
->canonicalID(canonicalID
);
830 ICUServiceFactory
* f
= createSimpleFactory(objToAdopt
, canonicalID
, visible
, status
);
832 return registerFactory(f
, status
);
840 ICUService::createSimpleFactory(UObject
* objToAdopt
, const UnicodeString
& id
, UBool visible
, UErrorCode
& status
)
842 if (U_SUCCESS(status
)) {
843 if ((objToAdopt
!= NULL
) && (!id
.isBogus())) {
844 return new SimpleFactory(objToAdopt
, id
, visible
);
846 status
= U_ILLEGAL_ARGUMENT_ERROR
;
852 ICUService::registerFactory(ICUServiceFactory
* factoryToAdopt
, UErrorCode
& status
)
854 if (U_SUCCESS(status
) && factoryToAdopt
!= NULL
) {
857 if (factories
== NULL
) {
858 factories
= new UVector(deleteUObject
, NULL
, status
);
859 if (U_FAILURE(status
)) {
864 factories
->insertElementAt(factoryToAdopt
, 0, status
);
865 if (U_SUCCESS(status
)) {
868 delete factoryToAdopt
;
869 factoryToAdopt
= NULL
;
873 if (factoryToAdopt
!= NULL
) {
877 return (URegistryKey
)factoryToAdopt
;
881 ICUService::unregister(URegistryKey rkey
, UErrorCode
& status
)
883 ICUServiceFactory
*factory
= (ICUServiceFactory
*)rkey
;
884 UBool result
= FALSE
;
885 if (factory
!= NULL
&& factories
!= NULL
) {
888 if (factories
->removeElement(factory
)) {
892 status
= U_ILLEGAL_ARGUMENT_ERROR
;
907 reInitializeFactories();
914 ICUService::reInitializeFactories()
916 if (factories
!= NULL
) {
917 factories
->removeAllElements();
922 ICUService::isDefault() const
924 return countFactories() == 0;
928 ICUService::createKey(const UnicodeString
* id
, UErrorCode
& status
) const
930 return (U_FAILURE(status
) || id
== NULL
) ? NULL
: new ICUServiceKey(*id
);
934 ICUService::clearCaches()
936 // callers synchronize before use
942 delete serviceCache
; serviceCache
= NULL
;
946 ICUService::clearServiceCache()
948 // callers synchronize before use
949 delete serviceCache
; serviceCache
= NULL
;
953 ICUService::acceptsListener(const EventListener
& l
) const
955 return dynamic_cast<const ServiceListener
*>(&l
) != NULL
;
959 ICUService::notifyListener(EventListener
& l
) const
961 ((ServiceListener
&)l
).serviceChanged(*this);
965 ICUService::getName(UnicodeString
& result
) const
967 return result
.append(name
);
971 ICUService::countFactories() const
973 return factories
== NULL
? 0 : factories
->size();
977 ICUService::getTimestamp() const
984 /* UCONFIG_NO_SERVICE */