1/**
2*******************************************************************************
3* Copyright (C) 2001-2008, 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
330ICUService::ICUService()
331: name()
332, lock(0)
333, timestamp(0)
334, factories(NULL)
335, serviceCache(NULL)
336, idCache(NULL)
337, dnCache(NULL)
338{
339    umtx_init(&lock);
340}
341
342ICUService::ICUService(const UnicodeString& newName)
343: name(newName)
344, lock(0)
345, timestamp(0)
346, factories(NULL)
347, serviceCache(NULL)
348, idCache(NULL)
349, dnCache(NULL)
350{
351    umtx_init(&lock);
352}
353
354ICUService::~ICUService()
355{
356    {
357        Mutex mutex(&lock);
358        clearCaches();
359        delete factories;
360        factories = NULL;
361    }
362    umtx_destroy(&lock);
363}
364
365UObject*
366ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
367{
368    return get(descriptor, NULL, status);
369}
370
371UObject*
372ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
373{
374    UObject* result = NULL;
375    ICUServiceKey* key = createKey(&descriptor, status);
376    if (key) {
377        result = getKey(*key, actualReturn, status);
378        delete key;
379    }
380    return result;
381}
382
383UObject*
384ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
385{
386    return getKey(key, NULL, status);
387}
388
389// this is a vector that subclasses of ICUService can override to further customize the result object
390// before returning it.  All other public get functions should call this one.
391
392UObject*
393ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
394{
395    return getKey(key, actualReturn, NULL, status);
396}
397
398// make it possible to call reentrantly on systems that don't have reentrant mutexes.
399// we can use this simple approach since we know the situation where we're calling
400// reentrantly even without knowing the thread.
401class XMutex : public UMemory {
402public:
403    inline XMutex(UMTX *mutex, UBool reentering)
404        : fMutex(mutex)
405        , fActive(!reentering)
406    {
407        if (fActive) umtx_lock(fMutex);
408    }
409    inline ~XMutex() {
410        if (fActive) umtx_unlock(fMutex);
411    }
412
413private:
414    UMTX  *fMutex;
415    UBool fActive;
416};
417
418struct UVectorDeleter {
419    UVector* _obj;
420    UVectorDeleter() : _obj(NULL) {}
421    ~UVectorDeleter() { delete _obj; }
422};
423
424// called only by factories, treat as private
425UObject*
426ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
427{
428    if (U_FAILURE(status)) {
429        return NULL;
430    }
431
432    if (isDefault()) {
433        return handleDefault(key, actualReturn, status);
434    }
435
436    ICUService* ncthis = (ICUService*)this; // cast away semantic const
437
438    CacheEntry* result = NULL;
439    {
440        // The factory list can't be modified until we're done,
441        // otherwise we might update the cache with an invalid result.
442        // The cache has to stay in synch with the factory list.
443        // ICU doesn't have monitors so we can't use rw locks, so
444        // we single-thread everything using this service, for now.
445
446        // if factory is not null, we're calling from within the mutex,
447        // and since some unix machines don't have reentrant mutexes we
448        // need to make sure not to try to lock it again.
449        XMutex mutex(&ncthis->lock, factory != NULL);
450
451        if (serviceCache == NULL) {
452            ncthis->serviceCache = new Hashtable(status);
453            if (ncthis->serviceCache == NULL) {
454                return NULL;
455            }
456            if (U_FAILURE(status)) {
457                delete serviceCache;
458                return NULL;
459            }
460            serviceCache->setValueDeleter(cacheDeleter);
461        }
462
463        UnicodeString currentDescriptor;
464        UVectorDeleter cacheDescriptorList;
465        UBool putInCache = FALSE;
466
467        int32_t startIndex = 0;
468        int32_t limit = factories->size();
469        UBool cacheResult = TRUE;
470
471        if (factory != NULL) {
472            for (int32_t i = 0; i < limit; ++i) {
473                if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
474                    startIndex = i + 1;
475                    break;
476                }
477            }
478            if (startIndex == 0) {
479                // throw new InternalError("Factory " + factory + "not registered with service: " + this);
480                status = U_ILLEGAL_ARGUMENT_ERROR;
481                return NULL;
482            }
483            cacheResult = FALSE;
484        }
485
486        do {
487            currentDescriptor.remove();
488            key.currentDescriptor(currentDescriptor);
489            result = (CacheEntry*)serviceCache->get(currentDescriptor);
490            if (result != NULL) {
491                break;
492            }
493
494            // first test of cache failed, so we'll have to update
495            // the cache if we eventually succeed-- that is, if we're
496            // going to update the cache at all.
497            putInCache = TRUE;
498
499            int32_t index = startIndex;
500            while (index < limit) {
501                ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
502                UObject* service = f->create(key, this, status);
503                if (U_FAILURE(status)) {
504                    delete service;
505                    return NULL;
506                }
507                if (service != NULL) {
508                    result = new CacheEntry(currentDescriptor, service);
509                    if (result == NULL) {
510                        delete service;
511                        status = U_MEMORY_ALLOCATION_ERROR;
512                        return NULL;
513                    }
514
515                    goto outerEnd;
516                }
517            }
518
519            // prepare to load the cache with all additional ids that
520            // will resolve to result, assuming we'll succeed.  We
521            // don't want to keep querying on an id that's going to
522            // fallback to the one that succeeded, we want to hit the
523            // cache the first time next goaround.
524            if (cacheDescriptorList._obj == NULL) {
525                cacheDescriptorList._obj = new UVector(uhash_deleteUnicodeString, NULL, 5, status);
526                if (U_FAILURE(status)) {
527                    return NULL;
528                }
529            }
530            UnicodeString* idToCache = new UnicodeString(currentDescriptor);
531            if (idToCache == NULL || idToCache->isBogus()) {
532                status = U_MEMORY_ALLOCATION_ERROR;
533                return NULL;
534            }
535
536            cacheDescriptorList._obj->addElement(idToCache, status);
537            if (U_FAILURE(status)) {
538                return NULL;
539            }
540        } while (key.fallback());
541outerEnd:
542
543        if (result != NULL) {
544            if (putInCache && cacheResult) {
545                serviceCache->put(result->actualDescriptor, result, status);
546                if (U_FAILURE(status)) {
547                    delete result;
548                    return NULL;
549                }
550
551                if (cacheDescriptorList._obj != NULL) {
552                    for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) {
553                        UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i);
554                        serviceCache->put(*desc, result, status);
555                        if (U_FAILURE(status)) {
556                            delete result;
557                            return NULL;
558                        }
559
560                        result->ref();
561                        cacheDescriptorList._obj->removeElementAt(i);
562                    }
563                }
564            }
565
566            if (actualReturn != NULL) {
567                // strip null prefix
568                if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/)
569                    actualReturn->remove();
570                    actualReturn->append(result->actualDescriptor,
571                        1,
572                        result->actualDescriptor.length() - 1);
573                } else {
574                    *actualReturn = result->actualDescriptor;
575                }
576
577                if (actualReturn->isBogus()) {
578                    status = U_MEMORY_ALLOCATION_ERROR;
579                    delete result;
580                    return NULL;
581                }
582            }
583
584            UObject* service = cloneInstance(result->service);
585            if (putInCache && !cacheResult) {
586                delete result;
587            }
588            return service;
589        }
590    }
591
592    return handleDefault(key, actualReturn, status);
593}
594
595UObject*
596ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
597{
598    return NULL;
599}
600
601UVector&
602ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
603    return getVisibleIDs(result, NULL, status);
604}
605
606UVector&
607ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
608{
609    result.removeAllElements();
610
611    if (U_FAILURE(status)) {
612        return result;
613    }
614
615    ICUService * ncthis = (ICUService*)this; // cast away semantic const
616    {
617        Mutex mutex(&ncthis->lock);
618        const Hashtable* map = getVisibleIDMap(status);
619        if (map != NULL) {
620            ICUServiceKey* fallbackKey = createKey(matchID, status);
621
622            for (int32_t pos = -1;;) {
623                const UHashElement* e = map->nextElement(pos);
624                if (e == NULL) {
625                    break;
626                }
627
628                const UnicodeString* id = (const UnicodeString*)e->key.pointer;
629                if (fallbackKey != NULL) {
630                    if (!fallbackKey->isFallbackOf(*id)) {
631                        continue;
632                    }
633                }
634
635                UnicodeString* idClone = new UnicodeString(*id);
636                if (idClone == NULL || idClone->isBogus()) {
637                    delete idClone;
638                    status = U_MEMORY_ALLOCATION_ERROR;
639                    break;
640                }
641                result.addElement(idClone, status);
642                if (U_FAILURE(status)) {
643                    delete idClone;
644                    break;
645                }
646            }
647            delete fallbackKey;
648        }
649    }
650    if (U_FAILURE(status)) {
651        result.removeAllElements();
652    }
653    return result;
654}
655
656const Hashtable*
657ICUService::getVisibleIDMap(UErrorCode& status) const {
658    if (U_FAILURE(status)) return NULL;
659
660    // must only be called when lock is already held
661
662    ICUService* ncthis = (ICUService*)this; // cast away semantic const
663    if (idCache == NULL) {
664        ncthis->idCache = new Hashtable(status);
665        if (idCache == NULL) {
666            status = U_MEMORY_ALLOCATION_ERROR;
667        } else if (factories != NULL) {
668            for (int32_t pos = factories->size(); --pos >= 0;) {
669                ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
670                f->updateVisibleIDs(*idCache, status);
671            }
672            if (U_FAILURE(status)) {
673                delete idCache;
674                ncthis->idCache = NULL;
675            }
676        }
677    }
678
679    return idCache;
680}
681
682
683UnicodeString&
684ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
685{
686    return getDisplayName(id, result, Locale::getDefault());
687}
688
689UnicodeString&
690ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
691{
692    {
693        ICUService* ncthis = (ICUService*)this; // cast away semantic const
694        UErrorCode status = U_ZERO_ERROR;
695        Mutex mutex(&ncthis->lock);
696        const Hashtable* map = getVisibleIDMap(status);
697        if (map != NULL) {
698            ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
699            if (f != NULL) {
700                f->getDisplayName(id, locale, result);
701                return result;
702            }
703
704            // fallback
705            UErrorCode status = U_ZERO_ERROR;
706            ICUServiceKey* fallbackKey = createKey(&id, status);
707            while (fallbackKey->fallback()) {
708                UnicodeString us;
709                fallbackKey->currentID(us);
710                f = (ICUServiceFactory*)map->get(us);
711                if (f != NULL) {
712                    f->getDisplayName(id, locale, result);
713                    delete fallbackKey;
714                    return result;
715                }
716            }
717            delete fallbackKey;
718        }
719    }
720    result.setToBogus();
721    return result;
722}
723
724UVector&
725ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
726{
727    return getDisplayNames(result, Locale::getDefault(), NULL, status);
728}
729
730
731UVector&
732ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
733{
734    return getDisplayNames(result, locale, NULL, status);
735}
736
737UVector&
738ICUService::getDisplayNames(UVector& result,
739                            const Locale& locale,
740                            const UnicodeString* matchID,
741                            UErrorCode& status) const
742{
743    result.removeAllElements();
744    result.setDeleter(userv_deleteStringPair);
745    if (U_SUCCESS(status)) {
746        ICUService* ncthis = (ICUService*)this; // cast away semantic const
747        Mutex mutex(&ncthis->lock);
748
749        if (dnCache != NULL && dnCache->locale != locale) {
750            delete dnCache;
751            ncthis->dnCache = NULL;
752        }
753
754        if (dnCache == NULL) {
755            const Hashtable* m = getVisibleIDMap(status);
756            if (m != NULL) {
757                ncthis->dnCache = new DNCache(locale);
758                if (dnCache == NULL) {
759                    status = U_MEMORY_ALLOCATION_ERROR;
760                    return result;
761                }
762
763                int32_t pos = -1;
764                const UHashElement* entry = NULL;
765                while ((entry = m->nextElement(pos)) != NULL) {
766                    const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
767                    ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
768                    UnicodeString dname;
769                    f->getDisplayName(*id, locale, dname);
770                    if (dname.isBogus()) {
771                        status = U_MEMORY_ALLOCATION_ERROR;
772                    } else {
773                        dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
774                        if (U_SUCCESS(status)) {
775                            continue;
776                        }
777                    }
778                    delete dnCache;
779                    ncthis->dnCache = NULL;
780                    return result;
781                }
782            }
783        }
784    }
785
786    ICUServiceKey* matchKey = createKey(matchID, status);
787    /* To ensure that all elements in the hashtable are iterated, set pos to -1.
788     * nextElement(pos) will skip the position at pos and begin the iteration
789     * at the next position, which in this case will be 0.
790     */
791    int32_t pos = -1;
792    const UHashElement *entry = NULL;
793    while ((entry = dnCache->cache.nextElement(pos)) != NULL) {
794        const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
795        if (matchKey != NULL && !matchKey->isFallbackOf(*id)) {
796            continue;
797        }
798        const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
799        StringPair* sp = StringPair::create(*id, *dn, status);
800        result.addElement(sp, status);
801        if (U_FAILURE(status)) {
802            result.removeAllElements();
803            break;
804        }
805    }
806    delete matchKey;
807
808    return result;
809}
810
811URegistryKey
812ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
813{
814    return registerInstance(objToAdopt, id, TRUE, status);
815}
816
817URegistryKey
818ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
819{
820    ICUServiceKey* key = createKey(&id, status);
821    if (key != NULL) {
822        UnicodeString canonicalID;
823        key->canonicalID(canonicalID);
824        delete key;
825
826        ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
827        if (f != NULL) {
828            return registerFactory(f, status);
829        }
830    }
831    delete objToAdopt;
832    return NULL;
833}
834
835ICUServiceFactory*
836ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
837{
838    if (U_SUCCESS(status)) {
839        if ((objToAdopt != NULL) && (!id.isBogus())) {
840            return new SimpleFactory(objToAdopt, id, visible);
841        }
842        status = U_ILLEGAL_ARGUMENT_ERROR;
843    }
844    return NULL;
845}
846
847URegistryKey
848ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
849{
850    if (U_SUCCESS(status) && factoryToAdopt != NULL) {
851        Mutex mutex(&lock);
852
853        if (factories == NULL) {
854            factories = new UVector(deleteUObject, NULL, status);
855            if (U_FAILURE(status)) {
856                delete factories;
857                return NULL;
858            }
859        }
860        factories->insertElementAt(factoryToAdopt, 0, status);
861        if (U_SUCCESS(status)) {
862            clearCaches();
863        } else {
864            delete factoryToAdopt;
865            factoryToAdopt = NULL;
866        }
867    }
868
869    if (factoryToAdopt != NULL) {
870        notifyChanged();
871    }
872
873    return (URegistryKey)factoryToAdopt;
874}
875
876UBool
877ICUService::unregister(URegistryKey rkey, UErrorCode& status)
878{
879    ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
880    UBool result = FALSE;
881    if (factory != NULL && factories != NULL) {
882        Mutex mutex(&lock);
883
884        if (factories->removeElement(factory)) {
885            clearCaches();
886            result = TRUE;
887        } else {
888            status = U_ILLEGAL_ARGUMENT_ERROR;
889            delete factory;
890        }
891    }
892    if (result) {
893        notifyChanged();
894    }
895    return result;
896}
897
898void
899ICUService::reset()
900{
901    {
902        Mutex mutex(&lock);
903        reInitializeFactories();
904        clearCaches();
905    }
906    notifyChanged();
907}
908
909void
910ICUService::reInitializeFactories()
911{
912    if (factories != NULL) {
913        factories->removeAllElements();
914    }
915}
916
917UBool
918ICUService::isDefault() const
919{
920    return countFactories() == 0;
921}
922
923ICUServiceKey*
924ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
925{
926    return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
927}
928
929void
930ICUService::clearCaches()
931{
932    // callers synchronize before use
933    ++timestamp;
934    delete dnCache;
935    dnCache = NULL;
936    delete idCache;
937    idCache = NULL;
938    delete serviceCache; serviceCache = NULL;
939}
940
941void
942ICUService::clearServiceCache()
943{
944    // callers synchronize before use
945    delete serviceCache; serviceCache = NULL;
946}
947
948UBool
949ICUService::acceptsListener(const EventListener& l) const
950{
951    return l.getDynamicClassID() == ServiceListener::getStaticClassID();
952}
953
954void
955ICUService::notifyListener(EventListener& l) const
956{
957    ((ServiceListener&)l).serviceChanged(*this);
958}
959
960UnicodeString&
961ICUService::getName(UnicodeString& result) const
962{
963    return result.append(name);
964}
965
966int32_t
967ICUService::countFactories() const
968{
969    return factories == NULL ? 0 : factories->size();
970}
971
972int32_t
973ICUService::getTimestamp() const
974{
975    return timestamp;
976}
977
978U_NAMESPACE_END
979
980/* UCONFIG_NO_SERVICE */
981#endif
982