• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 U_NAMESPACE_BEGIN
20 
21 /*
22 ******************************************************************
23 */
24 
25 const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F;   /* '/' */
26 
ICUServiceKey(const UnicodeString & id)27 ICUServiceKey::ICUServiceKey(const UnicodeString& id)
28 : _id(id) {
29 }
30 
~ICUServiceKey()31 ICUServiceKey::~ICUServiceKey()
32 {
33 }
34 
35 const UnicodeString&
getID() const36 ICUServiceKey::getID() const
37 {
38     return _id;
39 }
40 
41 UnicodeString&
canonicalID(UnicodeString & result) const42 ICUServiceKey::canonicalID(UnicodeString& result) const
43 {
44     return result.append(_id);
45 }
46 
47 UnicodeString&
currentID(UnicodeString & result) const48 ICUServiceKey::currentID(UnicodeString& result) const
49 {
50     return canonicalID(result);
51 }
52 
53 UnicodeString&
currentDescriptor(UnicodeString & result) const54 ICUServiceKey::currentDescriptor(UnicodeString& result) const
55 {
56     prefix(result);
57     result.append(PREFIX_DELIMITER);
58     return currentID(result);
59 }
60 
61 UBool
fallback()62 ICUServiceKey::fallback()
63 {
64     return FALSE;
65 }
66 
67 UBool
isFallbackOf(const UnicodeString & id) const68 ICUServiceKey::isFallbackOf(const UnicodeString& id) const
69 {
70     return id == _id;
71 }
72 
73 UnicodeString&
prefix(UnicodeString & result) const74 ICUServiceKey::prefix(UnicodeString& result) const
75 {
76     return result;
77 }
78 
79 UnicodeString&
parsePrefix(UnicodeString & result)80 ICUServiceKey::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 
90 UnicodeString&
parseSuffix(UnicodeString & result)91 ICUServiceKey::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
101 UnicodeString&
debug(UnicodeString & result) const102 ICUServiceKey::debug(UnicodeString& result) const
103 {
104     debugClass(result);
105     result.append(" id: ");
106     result.append(_id);
107     return result;
108 }
109 
110 UnicodeString&
debugClass(UnicodeString & result) const111 ICUServiceKey::debugClass(UnicodeString& result) const
112 {
113     return result.append("ICUServiceKey");
114 }
115 #endif
116 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)117 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
118 
119 /*
120 ******************************************************************
121 */
122 
123 SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
124 : _instance(instanceToAdopt), _id(id), _visible(visible)
125 {
126 }
127 
~SimpleFactory()128 SimpleFactory::~SimpleFactory()
129 {
130     delete _instance;
131 }
132 
133 UObject*
create(const ICUServiceKey & key,const ICUService * service,UErrorCode & status) const134 SimpleFactory::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 
145 void
updateVisibleIDs(Hashtable & result,UErrorCode & status) const146 SimpleFactory::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 
155 UnicodeString&
getDisplayName(const UnicodeString & id,const Locale &,UnicodeString & result) const156 SimpleFactory::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
167 UnicodeString&
debug(UnicodeString & toAppendTo) const168 SimpleFactory::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 
178 UnicodeString&
debugClass(UnicodeString & toAppendTo) const179 SimpleFactory::debugClass(UnicodeString& toAppendTo) const
180 {
181     return toAppendTo.append("SimpleFactory");
182 }
183 #endif
184 
185 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
186 
187 /*
188 ******************************************************************
189 */
190 
191 UOBJECT_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.
199 class CacheEntry : public UMemory {
200 private:
201     int32_t refcount;
202 
203 public:
204     UnicodeString actualDescriptor;
205     UObject* service;
206 
207     /**
208     * Releases a reference to the shared resource.
209     */
~CacheEntry()210     ~CacheEntry() {
211         delete service;
212     }
213 
CacheEntry(const UnicodeString & _actualDescriptor,UObject * _service)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     */
ref()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     */
unref()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     */
isShared() const248     UBool isShared() const {
249         return refcount > 1;
250     }
251 };
252 
253 // UObjectDeleter for serviceCache
254 U_CDECL_BEGIN
255 static void U_CALLCONV
cacheDeleter(void * obj)256 cacheDeleter(void* obj) {
257     U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
258 }
259 
260 /**
261 * Deleter for UObjects
262 */
263 static void U_CALLCONV
deleteUObject(void * obj)264 deleteUObject(void *obj) {
265     U_NAMESPACE_USE delete (UObject*) obj;
266 }
267 U_CDECL_END
268 
269 /*
270 ******************************************************************
271 */
272 
273 class DNCache : public UMemory {
274 public:
275     Hashtable cache;
276     const Locale locale;
277 
DNCache(const Locale & _locale)278     DNCache(const Locale& _locale)
279         : cache(), locale(_locale)
280     {
281         // cache.setKeyDeleter(uhash_deleteUnicodeString);
282     }
283 };
284 
285 
286 /*
287 ******************************************************************
288 */
289 
290 StringPair*
create(const UnicodeString & displayName,const UnicodeString & id,UErrorCode & status)291 StringPair::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 
307 UBool
isBogus() const308 StringPair::isBogus() const {
309     return displayName.isBogus() || id.isBogus();
310 }
311 
StringPair(const UnicodeString & _displayName,const UnicodeString & _id)312 StringPair::StringPair(const UnicodeString& _displayName,
313                        const UnicodeString& _id)
314 : displayName(_displayName)
315 , id(_id)
316 {
317 }
318 
319 U_CDECL_BEGIN
320 static void U_CALLCONV
userv_deleteStringPair(void * obj)321 userv_deleteStringPair(void *obj) {
322     U_NAMESPACE_USE delete (StringPair*) obj;
323 }
324 U_CDECL_END
325 
326 /*
327 ******************************************************************
328 */
329 
ICUService()330 ICUService::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 
ICUService(const UnicodeString & newName)342 ICUService::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 
~ICUService()354 ICUService::~ICUService()
355 {
356     {
357         Mutex mutex(&lock);
358         clearCaches();
359         delete factories;
360         factories = NULL;
361     }
362     umtx_destroy(&lock);
363 }
364 
365 UObject*
get(const UnicodeString & descriptor,UErrorCode & status) const366 ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
367 {
368     return get(descriptor, NULL, status);
369 }
370 
371 UObject*
get(const UnicodeString & descriptor,UnicodeString * actualReturn,UErrorCode & status) const372 ICUService::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 
383 UObject*
getKey(ICUServiceKey & key,UErrorCode & status) const384 ICUService::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 
392 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,UErrorCode & status) const393 ICUService::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.
401 class XMutex : public UMemory {
402 public:
XMutex(UMTX * mutex,UBool reentering)403     inline XMutex(UMTX *mutex, UBool reentering)
404         : fMutex(mutex)
405         , fActive(!reentering)
406     {
407         if (fActive) umtx_lock(fMutex);
408     }
~XMutex()409     inline ~XMutex() {
410         if (fActive) umtx_unlock(fMutex);
411     }
412 
413 private:
414     UMTX  *fMutex;
415     UBool fActive;
416 };
417 
418 struct UVectorDeleter {
419     UVector* _obj;
UVectorDeleterUVectorDeleter420     UVectorDeleter() : _obj(NULL) {}
~UVectorDeleterUVectorDeleter421     ~UVectorDeleter() { delete _obj; }
422 };
423 
424 // called only by factories, treat as private
425 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,const ICUServiceFactory * factory,UErrorCode & status) const426 ICUService::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());
541 outerEnd:
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 
595 UObject*
handleDefault(const ICUServiceKey &,UnicodeString *,UErrorCode &) const596 ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
597 {
598     return NULL;
599 }
600 
601 UVector&
getVisibleIDs(UVector & result,UErrorCode & status) const602 ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
603     return getVisibleIDs(result, NULL, status);
604 }
605 
606 UVector&
getVisibleIDs(UVector & result,const UnicodeString * matchID,UErrorCode & status) const607 ICUService::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 
656 const Hashtable*
getVisibleIDMap(UErrorCode & status) const657 ICUService::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 
683 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result) const684 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
685 {
686     return getDisplayName(id, result, Locale::getDefault());
687 }
688 
689 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result,const Locale & locale) const690 ICUService::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 
724 UVector&
getDisplayNames(UVector & result,UErrorCode & status) const725 ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
726 {
727     return getDisplayNames(result, Locale::getDefault(), NULL, status);
728 }
729 
730 
731 UVector&
getDisplayNames(UVector & result,const Locale & locale,UErrorCode & status) const732 ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
733 {
734     return getDisplayNames(result, locale, NULL, status);
735 }
736 
737 UVector&
getDisplayNames(UVector & result,const Locale & locale,const UnicodeString * matchID,UErrorCode & status) const738 ICUService::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 
811 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UErrorCode & status)812 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
813 {
814     return registerInstance(objToAdopt, id, TRUE, status);
815 }
816 
817 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)818 ICUService::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 
835 ICUServiceFactory*
createSimpleFactory(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)836 ICUService::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 
847 URegistryKey
registerFactory(ICUServiceFactory * factoryToAdopt,UErrorCode & status)848 ICUService::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 
876 UBool
unregister(URegistryKey rkey,UErrorCode & status)877 ICUService::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 
898 void
reset()899 ICUService::reset()
900 {
901     {
902         Mutex mutex(&lock);
903         reInitializeFactories();
904         clearCaches();
905     }
906     notifyChanged();
907 }
908 
909 void
reInitializeFactories()910 ICUService::reInitializeFactories()
911 {
912     if (factories != NULL) {
913         factories->removeAllElements();
914     }
915 }
916 
917 UBool
isDefault() const918 ICUService::isDefault() const
919 {
920     return countFactories() == 0;
921 }
922 
923 ICUServiceKey*
createKey(const UnicodeString * id,UErrorCode & status) const924 ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
925 {
926     return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
927 }
928 
929 void
clearCaches()930 ICUService::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 
941 void
clearServiceCache()942 ICUService::clearServiceCache()
943 {
944     // callers synchronize before use
945     delete serviceCache; serviceCache = NULL;
946 }
947 
948 UBool
acceptsListener(const EventListener & l) const949 ICUService::acceptsListener(const EventListener& l) const
950 {
951     return l.getDynamicClassID() == ServiceListener::getStaticClassID();
952 }
953 
954 void
notifyListener(EventListener & l) const955 ICUService::notifyListener(EventListener& l) const
956 {
957     ((ServiceListener&)l).serviceChanged(*this);
958 }
959 
960 UnicodeString&
getName(UnicodeString & result) const961 ICUService::getName(UnicodeString& result) const
962 {
963     return result.append(name);
964 }
965 
966 int32_t
countFactories() const967 ICUService::countFactories() const
968 {
969     return factories == NULL ? 0 : factories->size();
970 }
971 
972 int32_t
getTimestamp() const973 ICUService::getTimestamp() const
974 {
975     return timestamp;
976 }
977 
978 U_NAMESPACE_END
979 
980 /* UCONFIG_NO_SERVICE */
981 #endif
982