1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/**
4*******************************************************************************
5* Copyright (C) 2001-2014, International Business Machines Corporation.
6* All Rights Reserved.
7*******************************************************************************
8*/
9
10#include "unicode/utypes.h"
11#include "unicode/localpointer.h"
12
13#if !UCONFIG_NO_SERVICE
14
15#include "serv.h"
16#include "umutex.h"
17
18#undef SERVICE_REFCOUNT
19
20// in case we use the refcount stuff
21
22U_NAMESPACE_BEGIN
23
24/*
25******************************************************************
26*/
27
28const char16_t ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */
29
30ICUServiceKey::ICUServiceKey(const UnicodeString& id)
31: _id(id) {
32}
33
34ICUServiceKey::~ICUServiceKey()
35{
36}
37
38const UnicodeString&
39ICUServiceKey::getID() const
40{
41 return _id;
42}
43
44UnicodeString&
45ICUServiceKey::canonicalID(UnicodeString& result) const
46{
47 return result.append(_id);
48}
49
50UnicodeString&
51ICUServiceKey::currentID(UnicodeString& result) const
52{
53 return canonicalID(result);
54}
55
56UnicodeString&
57ICUServiceKey::currentDescriptor(UnicodeString& result) const
58{
59 prefix(result);
60 result.append(PREFIX_DELIMITER);
61 return currentID(result);
62}
63
64UBool
65ICUServiceKey::fallback()
66{
67 return false;
68}
69
70UBool
71ICUServiceKey::isFallbackOf(const UnicodeString& id) const
72{
73 return id == _id;
74}
75
76UnicodeString&
77ICUServiceKey::prefix(UnicodeString& result) const
78{
79 return result;
80}
81
82UnicodeString&
83ICUServiceKey::parsePrefix(UnicodeString& result)
84{
85 int32_t n = result.indexOf(PREFIX_DELIMITER);
86 if (n < 0) {
87 n = 0;
88 }
89 result.remove(n);
90 return result;
91}
92
93UnicodeString&
94ICUServiceKey::parseSuffix(UnicodeString& result)
95{
96 int32_t n = result.indexOf(PREFIX_DELIMITER);
97 if (n >= 0) {
98 result.remove(0, n+1);
99 }
100 return result;
101}
102
103#ifdef SERVICE_DEBUG
104UnicodeString&
105ICUServiceKey::debug(UnicodeString& result) const
106{
107 debugClass(result);
108 result.append((UnicodeString)" id: ");
109 result.append(_id);
110 return result;
111}
112
113UnicodeString&
114ICUServiceKey::debugClass(UnicodeString& result) const
115{
116 return result.append((UnicodeString)"ICUServiceKey");
117}
118#endif
119
120UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
121
122/*
123******************************************************************
124*/
125
126ICUServiceFactory::~ICUServiceFactory() {}
127
128SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
129: _instance(instanceToAdopt), _id(id), _visible(visible)
130{
131}
132
133SimpleFactory::~SimpleFactory()
134{
135 delete _instance;
136}
137
138UObject*
139SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
140{
141 if (U_SUCCESS(status)) {
142 UnicodeString temp;
143 if (_id == key.currentID(temp)) {
144 return service->cloneInstance(_instance);
145 }
146 }
147 return nullptr;
148}
149
150void
151SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
152{
153 if (_visible) {
154 result.put(_id, (void*)this, status); // cast away const
155 } else {
156 result.remove(_id);
157 }
158}
159
160UnicodeString&
161SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const
162{
163 if (_visible && _id == id) {
164 result = _id;
165 } else {
166 result.setToBogus();
167 }
168 return result;
169}
170
171#ifdef SERVICE_DEBUG
172UnicodeString&
173SimpleFactory::debug(UnicodeString& toAppendTo) const
174{
175 debugClass(toAppendTo);
176 toAppendTo.append((UnicodeString)" id: ");
177 toAppendTo.append(_id);
178 toAppendTo.append((UnicodeString)", visible: ");
179 toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F");
180 return toAppendTo;
181}
182
183UnicodeString&
184SimpleFactory::debugClass(UnicodeString& toAppendTo) const
185{
186 return toAppendTo.append((UnicodeString)"SimpleFactory");
187}
188#endif
189
190UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
191
192/*
193******************************************************************
194*/
195
196ServiceListener::~ServiceListener() {}
197
198UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
199
200/*
201******************************************************************
202*/
203
204// Record the actual id for this service in the cache, so we can return it
205// even if we succeed later with a different id.
206class CacheEntry : public UMemory {
207private:
208 int32_t refcount;
209
210public:
211 UnicodeString actualDescriptor;
212 UObject* service;
213
214 /**
215 * Releases a reference to the shared resource.
216 */
217 ~CacheEntry() {
218 delete service;
219 }
220
221 CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service)
222 : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
223 }
224
225 /**
226 * Instantiation creates an initial reference, so don't call this
227 * unless you're creating a new pointer to this. Management of
228 * that pointer will have to know how to deal with refcounts.
229 * Return true if the resource has not already been released.
230 */
231 CacheEntry* ref() {
232 ++refcount;
233 return this;
234 }
235
236 /**
237 * Destructions removes a reference, so don't call this unless
238 * you're removing pointer to this somewhere. Management of that
239 * pointer will have to know how to deal with refcounts. Once
240 * the refcount drops to zero, the resource is released. Return
241 * false if the resource has been released.
242 */
243 CacheEntry* unref() {
244 if ((--refcount) == 0) {
245 delete this;
246 return nullptr;
247 }
248 return this;
249 }
250
251 /**
252 * Return true if there is at least one reference to this and the
253 * resource has not been released.
254 */
255 UBool isShared() const {
256 return refcount > 1;
257 }
258};
259
260// Deleter for serviceCache
261U_CDECL_BEGIN
262static void U_CALLCONV
263cacheDeleter(void* obj) {
264 U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
265}
266
267U_CDECL_END
268
269/*
270******************************************************************
271*/
272
273class DNCache : public UMemory {
274public:
275 Hashtable cache;
276 const Locale locale;
277
278 DNCache(const Locale& _locale)
279 : cache(), locale(_locale)
280 {
281 // cache.setKeyDeleter(uprv_deleteUObject);
282 }
283};
284
285
286/*
287******************************************************************
288*/
289
290StringPair*
291StringPair::create(const UnicodeString& displayName,
292 const UnicodeString& id,
293 UErrorCode& status)
294{
295 if (U_SUCCESS(status)) {
296 StringPair* sp = new StringPair(displayName, id);
297 if (sp == nullptr || sp->isBogus()) {
298 status = U_MEMORY_ALLOCATION_ERROR;
299 delete sp;
300 return nullptr;
301 }
302 return sp;
303 }
304 return nullptr;
305}
306
307UBool
308StringPair::isBogus() const {
309 return displayName.isBogus() || id.isBogus();
310}
311
312StringPair::StringPair(const UnicodeString& _displayName,
313 const UnicodeString& _id)
314: displayName(_displayName)
315, id(_id)
316{
317}
318
319U_CDECL_BEGIN
320static void U_CALLCONV
321userv_deleteStringPair(void *obj) {
322 U_NAMESPACE_USE delete (StringPair*) obj;
323}
324U_CDECL_END
325
326/*
327******************************************************************
328*/
329
330static UMutex lock;
331
332ICUService::ICUService()
333: name()
334, timestamp(0)
335, factories(nullptr)
336, serviceCache(nullptr)
337, idCache(nullptr)
338, dnCache(nullptr)
339{
340}
341
342ICUService::ICUService(const UnicodeString& newName)
343: name(newName)
344, timestamp(0)
345, factories(nullptr)
346, serviceCache(nullptr)
347, idCache(nullptr)
348, dnCache(nullptr)
349{
350}
351
352ICUService::~ICUService()
353{
354 {
355 Mutex mutex(&lock);
356 clearCaches();
357 delete factories;
358 factories = nullptr;
359 }
360}
361
362UObject*
363ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
364{
365 return get(descriptor, nullptr, status);
366}
367
368UObject*
369ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
370{
371 UObject* result = nullptr;
372 ICUServiceKey* key = createKey(&descriptor, status);
373 if (key) {
374 result = getKey(*key, actualReturn, status);
375 delete key;
376 }
377 return result;
378}
379
380UObject*
381ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
382{
383 return getKey(key, nullptr, status);
384}
385
386// this is a vector that subclasses of ICUService can override to further customize the result object
387// before returning it. All other public get functions should call this one.
388
389UObject*
390ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
391{
392 return getKey(key, actualReturn, nullptr, status);
393}
394
395// make it possible to call reentrantly on systems that don't have reentrant mutexes.
396// we can use this simple approach since we know the situation where we're calling
397// reentrantly even without knowing the thread.
398class XMutex : public UMemory {
399public:
400 inline XMutex(UMutex *mutex, UBool reentering)
401 : fMutex(mutex)
402 , fActive(!reentering)
403 {
404 if (fActive) umtx_lock(fMutex);
405 }
406 inline ~XMutex() {
407 if (fActive) umtx_unlock(fMutex);
408 }
409
410private:
411 UMutex *fMutex;
412 UBool fActive;
413};
414
415// called only by factories, treat as private
416UObject*
417ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
418{
419 if (U_FAILURE(status)) {
420 return nullptr;
421 }
422
423 if (isDefault()) {
424 return handleDefault(key, actualReturn, status);
425 }
426
427 ICUService* ncthis = (ICUService*)this; // cast away semantic const
428
429 CacheEntry* result = nullptr;
430 {
431 // The factory list can't be modified until we're done,
432 // otherwise we might update the cache with an invalid result.
433 // The cache has to stay in synch with the factory list.
434 // ICU doesn't have monitors so we can't use rw locks, so
435 // we single-thread everything using this service, for now.
436
437 // if factory is not null, we're calling from within the mutex,
438 // and since some unix machines don't have reentrant mutexes we
439 // need to make sure not to try to lock it again.
440 XMutex mutex(&lock, factory != nullptr);
441
442 if (serviceCache == nullptr) {
443 ncthis->serviceCache = new Hashtable(status);
444 if (ncthis->serviceCache == nullptr) {
445 status = U_MEMORY_ALLOCATION_ERROR;
446 return nullptr;
447 }
448 if (U_FAILURE(status)) {
449 delete serviceCache;
450 return nullptr;
451 }
452 serviceCache->setValueDeleter(cacheDeleter);
453 }
454
455 UnicodeString currentDescriptor;
456 LocalPointer<UVector> cacheDescriptorList;
457 UBool putInCache = false;
458
459 int32_t startIndex = 0;
460 int32_t limit = factories->size();
461 UBool cacheResult = true;
462
463 if (factory != nullptr) {
464 for (int32_t i = 0; i < limit; ++i) {
465 if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
466 startIndex = i + 1;
467 break;
468 }
469 }
470 if (startIndex == 0) {
471 // throw new InternalError("Factory " + factory + "not registered with service: " + this);
472 status = U_ILLEGAL_ARGUMENT_ERROR;
473 return nullptr;
474 }
475 cacheResult = false;
476 }
477
478 do {
479 currentDescriptor.remove();
480 key.currentDescriptor(currentDescriptor);
481 result = (CacheEntry*)serviceCache->get(currentDescriptor);
482 if (result != nullptr) {
483 break;
484 }
485
486 // first test of cache failed, so we'll have to update
487 // the cache if we eventually succeed-- that is, if we're
488 // going to update the cache at all.
489 putInCache = true;
490
491 int32_t index = startIndex;
492 while (index < limit) {
493 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
494 LocalPointer<UObject> service(f->create(key, this, status));
495 if (U_FAILURE(status)) {
496 return nullptr;
497 }
498 if (service.isValid()) {
499 result = new CacheEntry(currentDescriptor, service.getAlias());
500 if (result == nullptr) {
501 status = U_MEMORY_ALLOCATION_ERROR;
502 return nullptr;
503 }
504 service.orphan(); // result now owns service.
505
506 goto outerEnd;
507 }
508 }
509
510 // prepare to load the cache with all additional ids that
511 // will resolve to result, assuming we'll succeed. We
512 // don't want to keep querying on an id that's going to
513 // fallback to the one that succeeded, we want to hit the
514 // cache the first time next goaround.
515 if (cacheDescriptorList.isNull()) {
516 cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, nullptr, 5, status), status);
517 if (U_FAILURE(status)) {
518 return nullptr;
519 }
520 }
521
522 LocalPointer<UnicodeString> idToCache(new UnicodeString(currentDescriptor), status);
523 if (U_FAILURE(status)) {
524 return nullptr;
525 }
526 if (idToCache->isBogus()) {
527 status = U_MEMORY_ALLOCATION_ERROR;
528 return nullptr;
529 }
530 cacheDescriptorList->adoptElement(idToCache.orphan(), status);
531 if (U_FAILURE(status)) {
532 return nullptr;
533 }
534 } while (key.fallback());
535outerEnd:
536
537 if (result != nullptr) {
538 if (putInCache && cacheResult) {
539 serviceCache->put(result->actualDescriptor, result, status);
540 if (U_FAILURE(status)) {
541 return nullptr;
542 }
543
544 if (cacheDescriptorList.isValid()) {
545 for (int32_t i = cacheDescriptorList->size(); --i >= 0;) {
546 UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i);
547
548 serviceCache->put(*desc, result, status);
549 if (U_FAILURE(status)) {
550 return nullptr;
551 }
552
553 result->ref();
554 cacheDescriptorList->removeElementAt(i);
555 }
556 }
557 }
558
559 if (actualReturn != nullptr) {
560 // strip null prefix
561 if (result->actualDescriptor.indexOf((char16_t)0x2f) == 0) { // U+002f=slash (/)
562 actualReturn->remove();
563 actualReturn->append(result->actualDescriptor,
564 1,
565 result->actualDescriptor.length() - 1);
566 } else {
567 *actualReturn = result->actualDescriptor;
568 }
569
570 if (actualReturn->isBogus()) {
571 status = U_MEMORY_ALLOCATION_ERROR;
572 delete result;
573 return nullptr;
574 }
575 }
576
577 UObject* service = cloneInstance(result->service);
578 if (putInCache && !cacheResult) {
579 delete result;
580 }
581 return service;
582 }
583 }
584
585 return handleDefault(key, actualReturn, status);
586}
587
588UObject*
589ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
590{
591 return nullptr;
592}
593
594UVector&
595ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
596 return getVisibleIDs(result, nullptr, status);
597}
598
599UVector&
600ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
601{
602 result.removeAllElements();
603
604 if (U_FAILURE(status)) {
605 return result;
606 }
607 UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject);
608
609 {
610 Mutex mutex(&lock);
611 const Hashtable* map = getVisibleIDMap(status);
612 if (map != nullptr) {
613 ICUServiceKey* fallbackKey = createKey(matchID, status);
614
615 for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) {
616 const UHashElement* e = map->nextElement(pos);
617 if (e == nullptr) {
618 break;
619 }
620
621 const UnicodeString* id = (const UnicodeString*)e->key.pointer;
622 if (fallbackKey != nullptr) {
623 if (!fallbackKey->isFallbackOf(*id)) {
624 continue;
625 }
626 }
627
628 LocalPointer<UnicodeString> idClone(id->clone(), status);
629 result.adoptElement(idClone.orphan(), status);
630 }
631 delete fallbackKey;
632 }
633 }
634 if (U_FAILURE(status)) {
635 result.removeAllElements();
636 }
637 result.setDeleter(savedDeleter);
638 return result;
639}
640
641const Hashtable*
642ICUService::getVisibleIDMap(UErrorCode& status) const {
643 if (U_FAILURE(status)) return nullptr;
644
645 // must only be called when lock is already held
646
647 ICUService* ncthis = (ICUService*)this; // cast away semantic const
648 if (idCache == nullptr) {
649 ncthis->idCache = new Hashtable(status);
650 if (idCache == nullptr) {
651 status = U_MEMORY_ALLOCATION_ERROR;
652 } else if (factories != nullptr) {
653 for (int32_t pos = factories->size(); --pos >= 0;) {
654 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
655 f->updateVisibleIDs(*idCache, status);
656 }
657 if (U_FAILURE(status)) {
658 delete idCache;
659 ncthis->idCache = nullptr;
660 }
661 }
662 }
663
664 return idCache;
665}
666
667
668UnicodeString&
669ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
670{
671 return getDisplayName(id, result, Locale::getDefault());
672}
673
674UnicodeString&
675ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
676{
677 {
678 UErrorCode status = U_ZERO_ERROR;
679 Mutex mutex(&lock);
680 const Hashtable* map = getVisibleIDMap(status);
681 if (map != nullptr) {
682 ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
683 if (f != nullptr) {
684 f->getDisplayName(id, locale, result);
685 return result;
686 }
687
688 // fallback
689 status = U_ZERO_ERROR;
690 ICUServiceKey* fallbackKey = createKey(&id, status);
691 while (fallbackKey != nullptr && fallbackKey->fallback()) {
692 UnicodeString us;
693 fallbackKey->currentID(us);
694 f = (ICUServiceFactory*)map->get(us);
695 if (f != nullptr) {
696 f->getDisplayName(id, locale, result);
697 delete fallbackKey;
698 return result;
699 }
700 }
701 delete fallbackKey;
702 }
703 }
704 result.setToBogus();
705 return result;
706}
707
708UVector&
709ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
710{
711 return getDisplayNames(result, Locale::getDefault(), nullptr, status);
712}
713
714
715UVector&
716ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
717{
718 return getDisplayNames(result, locale, nullptr, status);
719}
720
721UVector&
722ICUService::getDisplayNames(UVector& result,
723 const Locale& locale,
724 const UnicodeString* matchID,
725 UErrorCode& status) const
726{
727 result.removeAllElements();
728 result.setDeleter(userv_deleteStringPair);
729 if (U_SUCCESS(status)) {
730 ICUService* ncthis = (ICUService*)this; // cast away semantic const
731 Mutex mutex(&lock);
732
733 if (dnCache != nullptr && dnCache->locale != locale) {
734 delete dnCache;
735 ncthis->dnCache = nullptr;
736 }
737
738 if (dnCache == nullptr) {
739 const Hashtable* m = getVisibleIDMap(status);
740 if (U_FAILURE(status)) {
741 return result;
742 }
743 ncthis->dnCache = new DNCache(locale);
744 if (dnCache == nullptr) {
745 status = U_MEMORY_ALLOCATION_ERROR;
746 return result;
747 }
748
749 int32_t pos = UHASH_FIRST;
750 const UHashElement* entry = nullptr;
751 while ((entry = m->nextElement(pos)) != nullptr) {
752 const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
753 ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
754 UnicodeString dname;
755 f->getDisplayName(*id, locale, dname);
756 if (dname.isBogus()) {
757 status = U_MEMORY_ALLOCATION_ERROR;
758 } else {
759 dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
760 if (U_SUCCESS(status)) {
761 continue;
762 }
763 }
764 delete dnCache;
765 ncthis->dnCache = nullptr;
766 return result;
767 }
768 }
769 }
770
771 ICUServiceKey* matchKey = createKey(matchID, status);
772 /* To ensure that all elements in the hashtable are iterated, set pos to -1.
773 * nextElement(pos) will skip the position at pos and begin the iteration
774 * at the next position, which in this case will be 0.
775 */
776 int32_t pos = UHASH_FIRST;
777 const UHashElement *entry = nullptr;
778 while ((entry = dnCache->cache.nextElement(pos)) != nullptr) {
779 const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
780 if (matchKey != nullptr && !matchKey->isFallbackOf(*id)) {
781 continue;
782 }
783 const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
784 StringPair* sp = StringPair::create(*id, *dn, status);
785 result.adoptElement(sp, status);
786 if (U_FAILURE(status)) {
787 result.removeAllElements();
788 break;
789 }
790 }
791 delete matchKey;
792
793 return result;
794}
795
796URegistryKey
797ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
798{
799 return registerInstance(objToAdopt, id, true, status);
800}
801
802URegistryKey
803ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
804{
805 ICUServiceKey* key = createKey(&id, status);
806 if (key != nullptr) {
807 UnicodeString canonicalID;
808 key->canonicalID(canonicalID);
809 delete key;
810
811 ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
812 if (f != nullptr) {
813 return registerFactory(f, status);
814 }
815 }
816 delete objToAdopt;
817 return nullptr;
818}
819
820ICUServiceFactory*
821ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
822{
823 if (U_SUCCESS(status)) {
824 if ((objToAdopt != nullptr) && (!id.isBogus())) {
825 return new SimpleFactory(objToAdopt, id, visible);
826 }
827 status = U_ILLEGAL_ARGUMENT_ERROR;
828 }
829 return nullptr;
830}
831
832URegistryKey
833ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
834{
835 LocalPointer<ICUServiceFactory>lpFactoryToAdopt(factoryToAdopt);
836 if (U_FAILURE(status) || factoryToAdopt == nullptr) {
837 return nullptr;
838 }
839 {
840 Mutex mutex(&lock);
841
842 if (factories == nullptr) {
843 LocalPointer<UVector> lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status);
844 if (U_FAILURE(status)) {
845 return nullptr;
846 }
847 factories = lpFactories.orphan();
848 }
849 factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status);
850 if (U_SUCCESS(status)) {
851 clearCaches();
852 }
853 } // Close of mutex lock block.
854
855 if (U_SUCCESS(status)) {
856 notifyChanged();
857 return (URegistryKey)factoryToAdopt;
858 } else {
859 return nullptr;
860 }
861}
862
863UBool
864ICUService::unregister(URegistryKey rkey, UErrorCode& status)
865{
866 ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
867 UBool result = false;
868 if (factory != nullptr && factories != nullptr) {
869 Mutex mutex(&lock);
870
871 if (factories->removeElement(factory)) {
872 clearCaches();
873 result = true;
874 } else {
875 status = U_ILLEGAL_ARGUMENT_ERROR;
876 delete factory;
877 }
878 }
879 if (result) {
880 notifyChanged();
881 }
882 return result;
883}
884
885void
886ICUService::reset()
887{
888 {
889 Mutex mutex(&lock);
890 reInitializeFactories();
891 clearCaches();
892 }
893 notifyChanged();
894}
895
896void
897ICUService::reInitializeFactories()
898{
899 if (factories != nullptr) {
900 factories->removeAllElements();
901 }
902}
903
904UBool
905ICUService::isDefault() const
906{
907 return countFactories() == 0;
908}
909
910ICUServiceKey*
911ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
912{
913 return (U_FAILURE(status) || id == nullptr) ? nullptr : new ICUServiceKey(*id);
914}
915
916void
917ICUService::clearCaches()
918{
919 // callers synchronize before use
920 ++timestamp;
921 delete dnCache;
922 dnCache = nullptr;
923 delete idCache;
924 idCache = nullptr;
925 delete serviceCache; serviceCache = nullptr;
926}
927
928void
929ICUService::clearServiceCache()
930{
931 // callers synchronize before use
932 delete serviceCache; serviceCache = nullptr;
933}
934
935UBool
936ICUService::acceptsListener(const EventListener& l) const
937{
938 return dynamic_cast<const ServiceListener*>(&l) != nullptr;
939}
940
941void
942ICUService::notifyListener(EventListener& l) const
943{
944 (static_cast<ServiceListener&>(l)).serviceChanged(*this);
945}
946
947UnicodeString&
948ICUService::getName(UnicodeString& result) const
949{
950 return result.append(name);
951}
952
953int32_t
954ICUService::countFactories() const
955{
956 return factories == nullptr ? 0 : factories->size();
957}
958
959int32_t
960ICUService::getTimestamp() const
961{
962 return timestamp;
963}
964
965U_NAMESPACE_END
966
967/* UCONFIG_NO_SERVICE */
968#endif
969