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