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