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