/src/icu/icu4c/source/common/serv.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | : _id(id) { |
32 | 0 | } |
33 | | |
34 | | ICUServiceKey::~ICUServiceKey() |
35 | 0 | { |
36 | 0 | } |
37 | | |
38 | | const UnicodeString& |
39 | | ICUServiceKey::getID() const |
40 | 0 | { |
41 | 0 | return _id; |
42 | 0 | } |
43 | | |
44 | | UnicodeString& |
45 | | ICUServiceKey::canonicalID(UnicodeString& result) const |
46 | 0 | { |
47 | 0 | return result.append(_id); |
48 | 0 | } |
49 | | |
50 | | UnicodeString& |
51 | | ICUServiceKey::currentID(UnicodeString& result) const |
52 | 0 | { |
53 | 0 | return canonicalID(result); |
54 | 0 | } |
55 | | |
56 | | UnicodeString& |
57 | | ICUServiceKey::currentDescriptor(UnicodeString& result) const |
58 | 0 | { |
59 | 0 | prefix(result); |
60 | 0 | result.append(PREFIX_DELIMITER); |
61 | 0 | return currentID(result); |
62 | 0 | } |
63 | | |
64 | | UBool |
65 | | ICUServiceKey::fallback() |
66 | 0 | { |
67 | 0 | return false; |
68 | 0 | } |
69 | | |
70 | | UBool |
71 | | ICUServiceKey::isFallbackOf(const UnicodeString& id) const |
72 | 0 | { |
73 | 0 | return id == _id; |
74 | 0 | } |
75 | | |
76 | | UnicodeString& |
77 | | ICUServiceKey::prefix(UnicodeString& result) const |
78 | 0 | { |
79 | 0 | return result; |
80 | 0 | } |
81 | | |
82 | | UnicodeString& |
83 | | ICUServiceKey::parsePrefix(UnicodeString& result) |
84 | 0 | { |
85 | 0 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
86 | 0 | if (n < 0) { |
87 | 0 | n = 0; |
88 | 0 | } |
89 | 0 | result.remove(n); |
90 | 0 | return result; |
91 | 0 | } |
92 | | |
93 | | UnicodeString& |
94 | | ICUServiceKey::parseSuffix(UnicodeString& result) |
95 | 0 | { |
96 | 0 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
97 | 0 | if (n >= 0) { |
98 | 0 | result.remove(0, n+1); |
99 | 0 | } |
100 | 0 | return result; |
101 | 0 | } |
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 | 0 | ICUServiceFactory::~ICUServiceFactory() {} |
127 | | |
128 | | SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) |
129 | 0 | : _instance(instanceToAdopt), _id(id), _visible(visible) |
130 | 0 | { |
131 | 0 | } |
132 | | |
133 | | SimpleFactory::~SimpleFactory() |
134 | 0 | { |
135 | 0 | delete _instance; |
136 | 0 | } |
137 | | |
138 | | UObject* |
139 | | SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const |
140 | 0 | { |
141 | 0 | if (U_SUCCESS(status)) { |
142 | 0 | UnicodeString temp; |
143 | 0 | if (_id == key.currentID(temp)) { |
144 | 0 | return service->cloneInstance(_instance); |
145 | 0 | } |
146 | 0 | } |
147 | 0 | return nullptr; |
148 | 0 | } |
149 | | |
150 | | void |
151 | | SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const |
152 | 0 | { |
153 | 0 | if (_visible) { |
154 | 0 | result.put(_id, (void*)this, status); // cast away const |
155 | 0 | } else { |
156 | 0 | result.remove(_id); |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | UnicodeString& |
161 | | SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const |
162 | 0 | { |
163 | 0 | if (_visible && _id == id) { |
164 | 0 | result = _id; |
165 | 0 | } else { |
166 | 0 | result.setToBogus(); |
167 | 0 | } |
168 | 0 | return result; |
169 | 0 | } |
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 | 0 | 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 | 0 | ~CacheEntry() { |
218 | 0 | delete service; |
219 | 0 | } |
220 | | |
221 | | CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) |
222 | 0 | : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { |
223 | 0 | } |
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 | 0 | CacheEntry* ref() { |
232 | 0 | ++refcount; |
233 | 0 | return this; |
234 | 0 | } |
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 | 0 | CacheEntry* unref() { |
244 | 0 | if ((--refcount) == 0) { |
245 | 0 | delete this; |
246 | 0 | return nullptr; |
247 | 0 | } |
248 | 0 | return this; |
249 | 0 | } |
250 | | |
251 | | /** |
252 | | * Return true if there is at least one reference to this and the |
253 | | * resource has not been released. |
254 | | */ |
255 | 0 | UBool isShared() const { |
256 | 0 | return refcount > 1; |
257 | 0 | } |
258 | | }; |
259 | | |
260 | | // Deleter for serviceCache |
261 | | U_CDECL_BEGIN |
262 | | static void U_CALLCONV |
263 | 0 | cacheDeleter(void* obj) { |
264 | 0 | U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); |
265 | 0 | } |
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 | 0 | : cache(), locale(_locale) |
280 | 0 | { |
281 | | // cache.setKeyDeleter(uprv_deleteUObject); |
282 | 0 | } |
283 | | }; |
284 | | |
285 | | |
286 | | /* |
287 | | ****************************************************************** |
288 | | */ |
289 | | |
290 | | StringPair* |
291 | | StringPair::create(const UnicodeString& displayName, |
292 | | const UnicodeString& id, |
293 | | UErrorCode& status) |
294 | 0 | { |
295 | 0 | if (U_SUCCESS(status)) { |
296 | 0 | StringPair* sp = new StringPair(displayName, id); |
297 | 0 | if (sp == nullptr || sp->isBogus()) { |
298 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
299 | 0 | delete sp; |
300 | 0 | return nullptr; |
301 | 0 | } |
302 | 0 | return sp; |
303 | 0 | } |
304 | 0 | return nullptr; |
305 | 0 | } |
306 | | |
307 | | UBool |
308 | 0 | StringPair::isBogus() const { |
309 | 0 | return displayName.isBogus() || id.isBogus(); |
310 | 0 | } |
311 | | |
312 | | StringPair::StringPair(const UnicodeString& _displayName, |
313 | | const UnicodeString& _id) |
314 | 0 | : displayName(_displayName) |
315 | 0 | , id(_id) |
316 | 0 | { |
317 | 0 | } |
318 | | |
319 | | U_CDECL_BEGIN |
320 | | static void U_CALLCONV |
321 | 0 | userv_deleteStringPair(void *obj) { |
322 | 0 | U_NAMESPACE_USE delete (StringPair*) obj; |
323 | 0 | } |
324 | | U_CDECL_END |
325 | | |
326 | | /* |
327 | | ****************************************************************** |
328 | | */ |
329 | | |
330 | | static UMutex lock; |
331 | | |
332 | | ICUService::ICUService() |
333 | 0 | : name() |
334 | 0 | , timestamp(0) |
335 | 0 | , factories(nullptr) |
336 | 0 | , serviceCache(nullptr) |
337 | 0 | , idCache(nullptr) |
338 | 0 | , dnCache(nullptr) |
339 | 0 | { |
340 | 0 | } |
341 | | |
342 | | ICUService::ICUService(const UnicodeString& newName) |
343 | 0 | : name(newName) |
344 | 0 | , timestamp(0) |
345 | 0 | , factories(nullptr) |
346 | 0 | , serviceCache(nullptr) |
347 | 0 | , idCache(nullptr) |
348 | 0 | , dnCache(nullptr) |
349 | 0 | { |
350 | 0 | } |
351 | | |
352 | | ICUService::~ICUService() |
353 | 0 | { |
354 | 0 | { |
355 | 0 | Mutex mutex(&lock); |
356 | 0 | clearCaches(); |
357 | 0 | delete factories; |
358 | 0 | factories = nullptr; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | UObject* |
363 | | ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const |
364 | 0 | { |
365 | 0 | return get(descriptor, nullptr, status); |
366 | 0 | } |
367 | | |
368 | | UObject* |
369 | | ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const |
370 | 0 | { |
371 | 0 | UObject* result = nullptr; |
372 | 0 | ICUServiceKey* key = createKey(&descriptor, status); |
373 | 0 | if (key) { |
374 | 0 | result = getKey(*key, actualReturn, status); |
375 | 0 | delete key; |
376 | 0 | } |
377 | 0 | return result; |
378 | 0 | } |
379 | | |
380 | | UObject* |
381 | | ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const |
382 | 0 | { |
383 | 0 | return getKey(key, nullptr, status); |
384 | 0 | } |
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 | 0 | { |
392 | 0 | return getKey(key, actualReturn, nullptr, status); |
393 | 0 | } |
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 | 0 | : fMutex(mutex) |
402 | 0 | , fActive(!reentering) |
403 | 0 | { |
404 | 0 | if (fActive) umtx_lock(fMutex); |
405 | 0 | } |
406 | 0 | inline ~XMutex() { |
407 | 0 | if (fActive) umtx_unlock(fMutex); |
408 | 0 | } |
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 | 0 | { |
419 | 0 | if (U_FAILURE(status)) { |
420 | 0 | return nullptr; |
421 | 0 | } |
422 | | |
423 | 0 | if (isDefault()) { |
424 | 0 | return handleDefault(key, actualReturn, status); |
425 | 0 | } |
426 | | |
427 | 0 | ICUService* ncthis = const_cast<ICUService*>(this); // cast away semantic const |
428 | |
|
429 | 0 | CacheEntry* result = nullptr; |
430 | 0 | { |
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 | 0 | XMutex mutex(&lock, factory != nullptr); |
441 | |
|
442 | 0 | if (serviceCache == nullptr) { |
443 | 0 | ncthis->serviceCache = new Hashtable(status); |
444 | 0 | if (ncthis->serviceCache == nullptr) { |
445 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
446 | 0 | return nullptr; |
447 | 0 | } |
448 | 0 | if (U_FAILURE(status)) { |
449 | 0 | delete serviceCache; |
450 | 0 | return nullptr; |
451 | 0 | } |
452 | 0 | serviceCache->setValueDeleter(cacheDeleter); |
453 | 0 | } |
454 | | |
455 | 0 | UnicodeString currentDescriptor; |
456 | 0 | LocalPointer<UVector> cacheDescriptorList; |
457 | 0 | UBool putInCache = false; |
458 | |
|
459 | 0 | int32_t startIndex = 0; |
460 | 0 | int32_t limit = factories->size(); |
461 | 0 | UBool cacheResult = true; |
462 | |
|
463 | 0 | if (factory != nullptr) { |
464 | 0 | for (int32_t i = 0; i < limit; ++i) { |
465 | 0 | if (factory == static_cast<const ICUServiceFactory*>(factories->elementAt(i))) { |
466 | 0 | startIndex = i + 1; |
467 | 0 | break; |
468 | 0 | } |
469 | 0 | } |
470 | 0 | if (startIndex == 0) { |
471 | | // throw new InternalError("Factory " + factory + "not registered with service: " + this); |
472 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
473 | 0 | return nullptr; |
474 | 0 | } |
475 | 0 | cacheResult = false; |
476 | 0 | } |
477 | | |
478 | 0 | do { |
479 | 0 | currentDescriptor.remove(); |
480 | 0 | key.currentDescriptor(currentDescriptor); |
481 | 0 | result = static_cast<CacheEntry*>(serviceCache->get(currentDescriptor)); |
482 | 0 | if (result != nullptr) { |
483 | 0 | break; |
484 | 0 | } |
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 | 0 | putInCache = true; |
490 | |
|
491 | 0 | int32_t index = startIndex; |
492 | 0 | while (index < limit) { |
493 | 0 | ICUServiceFactory* f = static_cast<ICUServiceFactory*>(factories->elementAt(index++)); |
494 | 0 | LocalPointer<UObject> service(f->create(key, this, status)); |
495 | 0 | if (U_FAILURE(status)) { |
496 | 0 | return nullptr; |
497 | 0 | } |
498 | 0 | if (service.isValid()) { |
499 | 0 | result = new CacheEntry(currentDescriptor, service.getAlias()); |
500 | 0 | if (result == nullptr) { |
501 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
502 | 0 | return nullptr; |
503 | 0 | } |
504 | 0 | service.orphan(); // result now owns service. |
505 | |
|
506 | 0 | goto outerEnd; |
507 | 0 | } |
508 | 0 | } |
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 | 0 | if (cacheDescriptorList.isNull()) { |
516 | 0 | cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, nullptr, 5, status), status); |
517 | 0 | if (U_FAILURE(status)) { |
518 | 0 | return nullptr; |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | 0 | LocalPointer<UnicodeString> idToCache(new UnicodeString(currentDescriptor), status); |
523 | 0 | if (U_FAILURE(status)) { |
524 | 0 | return nullptr; |
525 | 0 | } |
526 | 0 | if (idToCache->isBogus()) { |
527 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
528 | 0 | return nullptr; |
529 | 0 | } |
530 | 0 | cacheDescriptorList->adoptElement(idToCache.orphan(), status); |
531 | 0 | if (U_FAILURE(status)) { |
532 | 0 | return nullptr; |
533 | 0 | } |
534 | 0 | } while (key.fallback()); |
535 | 0 | outerEnd: |
536 | |
|
537 | 0 | if (result != nullptr) { |
538 | 0 | if (putInCache && cacheResult) { |
539 | 0 | serviceCache->put(result->actualDescriptor, result, status); |
540 | 0 | if (U_FAILURE(status)) { |
541 | 0 | return nullptr; |
542 | 0 | } |
543 | | |
544 | 0 | if (cacheDescriptorList.isValid()) { |
545 | 0 | for (int32_t i = cacheDescriptorList->size(); --i >= 0;) { |
546 | 0 | UnicodeString* desc = static_cast<UnicodeString*>(cacheDescriptorList->elementAt(i)); |
547 | |
|
548 | 0 | serviceCache->put(*desc, result, status); |
549 | 0 | if (U_FAILURE(status)) { |
550 | 0 | return nullptr; |
551 | 0 | } |
552 | | |
553 | 0 | result->ref(); |
554 | 0 | cacheDescriptorList->removeElementAt(i); |
555 | 0 | } |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | 0 | if (actualReturn != nullptr) { |
560 | | // strip null prefix |
561 | 0 | if (result->actualDescriptor.indexOf(static_cast<char16_t>(0x2f)) == 0) { // U+002f=slash (/) |
562 | 0 | actualReturn->remove(); |
563 | 0 | actualReturn->append(result->actualDescriptor, |
564 | 0 | 1, |
565 | 0 | result->actualDescriptor.length() - 1); |
566 | 0 | } else { |
567 | 0 | *actualReturn = result->actualDescriptor; |
568 | 0 | } |
569 | |
|
570 | 0 | if (actualReturn->isBogus()) { |
571 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
572 | 0 | delete result; |
573 | 0 | return nullptr; |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | 0 | UObject* service = cloneInstance(result->service); |
578 | 0 | if (putInCache && !cacheResult) { |
579 | 0 | delete result; |
580 | 0 | } |
581 | 0 | return service; |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | 0 | return handleDefault(key, actualReturn, status); |
586 | 0 | } |
587 | | |
588 | | UObject* |
589 | | ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const |
590 | 0 | { |
591 | 0 | return nullptr; |
592 | 0 | } |
593 | | |
594 | | UVector& |
595 | 0 | ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { |
596 | 0 | return getVisibleIDs(result, nullptr, status); |
597 | 0 | } |
598 | | |
599 | | UVector& |
600 | | ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const |
601 | 0 | { |
602 | 0 | result.removeAllElements(); |
603 | |
|
604 | 0 | if (U_FAILURE(status)) { |
605 | 0 | return result; |
606 | 0 | } |
607 | 0 | UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject); |
608 | |
|
609 | 0 | { |
610 | 0 | Mutex mutex(&lock); |
611 | 0 | const Hashtable* map = getVisibleIDMap(status); |
612 | 0 | if (map != nullptr) { |
613 | 0 | ICUServiceKey* fallbackKey = createKey(matchID, status); |
614 | |
|
615 | 0 | for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) { |
616 | 0 | const UHashElement* e = map->nextElement(pos); |
617 | 0 | if (e == nullptr) { |
618 | 0 | break; |
619 | 0 | } |
620 | | |
621 | 0 | const UnicodeString* id = static_cast<const UnicodeString*>(e->key.pointer); |
622 | 0 | if (fallbackKey != nullptr) { |
623 | 0 | if (!fallbackKey->isFallbackOf(*id)) { |
624 | 0 | continue; |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | 0 | LocalPointer<UnicodeString> idClone(id->clone(), status); |
629 | 0 | result.adoptElement(idClone.orphan(), status); |
630 | 0 | } |
631 | 0 | delete fallbackKey; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | if (U_FAILURE(status)) { |
635 | 0 | result.removeAllElements(); |
636 | 0 | } |
637 | 0 | result.setDeleter(savedDeleter); |
638 | 0 | return result; |
639 | 0 | } |
640 | | |
641 | | const Hashtable* |
642 | 0 | ICUService::getVisibleIDMap(UErrorCode& status) const { |
643 | 0 | if (U_FAILURE(status)) return nullptr; |
644 | | |
645 | | // must only be called when lock is already held |
646 | | |
647 | 0 | ICUService* ncthis = const_cast<ICUService*>(this); // cast away semantic const |
648 | 0 | if (idCache == nullptr) { |
649 | 0 | ncthis->idCache = new Hashtable(status); |
650 | 0 | if (idCache == nullptr) { |
651 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
652 | 0 | } else if (factories != nullptr) { |
653 | 0 | for (int32_t pos = factories->size(); --pos >= 0;) { |
654 | 0 | ICUServiceFactory* f = static_cast<ICUServiceFactory*>(factories->elementAt(pos)); |
655 | 0 | f->updateVisibleIDs(*idCache, status); |
656 | 0 | } |
657 | 0 | if (U_FAILURE(status)) { |
658 | 0 | delete idCache; |
659 | 0 | ncthis->idCache = nullptr; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | } |
663 | |
|
664 | 0 | return idCache; |
665 | 0 | } |
666 | | |
667 | | |
668 | | UnicodeString& |
669 | | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const |
670 | 0 | { |
671 | 0 | return getDisplayName(id, result, Locale::getDefault()); |
672 | 0 | } |
673 | | |
674 | | UnicodeString& |
675 | | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const |
676 | 0 | { |
677 | 0 | { |
678 | 0 | UErrorCode status = U_ZERO_ERROR; |
679 | 0 | Mutex mutex(&lock); |
680 | 0 | const Hashtable* map = getVisibleIDMap(status); |
681 | 0 | if (map != nullptr) { |
682 | 0 | ICUServiceFactory* f = static_cast<ICUServiceFactory*>(map->get(id)); |
683 | 0 | if (f != nullptr) { |
684 | 0 | f->getDisplayName(id, locale, result); |
685 | 0 | return result; |
686 | 0 | } |
687 | | |
688 | | // fallback |
689 | 0 | status = U_ZERO_ERROR; |
690 | 0 | ICUServiceKey* fallbackKey = createKey(&id, status); |
691 | 0 | while (fallbackKey != nullptr && fallbackKey->fallback()) { |
692 | 0 | UnicodeString us; |
693 | 0 | fallbackKey->currentID(us); |
694 | 0 | f = static_cast<ICUServiceFactory*>(map->get(us)); |
695 | 0 | if (f != nullptr) { |
696 | 0 | f->getDisplayName(id, locale, result); |
697 | 0 | delete fallbackKey; |
698 | 0 | return result; |
699 | 0 | } |
700 | 0 | } |
701 | 0 | delete fallbackKey; |
702 | 0 | } |
703 | 0 | } |
704 | 0 | result.setToBogus(); |
705 | 0 | return result; |
706 | 0 | } |
707 | | |
708 | | UVector& |
709 | | ICUService::getDisplayNames(UVector& result, UErrorCode& status) const |
710 | 0 | { |
711 | 0 | return getDisplayNames(result, Locale::getDefault(), nullptr, status); |
712 | 0 | } |
713 | | |
714 | | |
715 | | UVector& |
716 | | ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const |
717 | 0 | { |
718 | 0 | return getDisplayNames(result, locale, nullptr, status); |
719 | 0 | } |
720 | | |
721 | | UVector& |
722 | | ICUService::getDisplayNames(UVector& result, |
723 | | const Locale& locale, |
724 | | const UnicodeString* matchID, |
725 | | UErrorCode& status) const |
726 | 0 | { |
727 | 0 | result.removeAllElements(); |
728 | 0 | result.setDeleter(userv_deleteStringPair); |
729 | 0 | if (U_SUCCESS(status)) { |
730 | 0 | ICUService* ncthis = const_cast<ICUService*>(this); // cast away semantic const |
731 | 0 | Mutex mutex(&lock); |
732 | |
|
733 | 0 | if (dnCache != nullptr && dnCache->locale != locale) { |
734 | 0 | delete dnCache; |
735 | 0 | ncthis->dnCache = nullptr; |
736 | 0 | } |
737 | |
|
738 | 0 | if (dnCache == nullptr) { |
739 | 0 | const Hashtable* m = getVisibleIDMap(status); |
740 | 0 | if (U_FAILURE(status)) { |
741 | 0 | return result; |
742 | 0 | } |
743 | 0 | ncthis->dnCache = new DNCache(locale); |
744 | 0 | if (dnCache == nullptr) { |
745 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
746 | 0 | return result; |
747 | 0 | } |
748 | | |
749 | 0 | int32_t pos = UHASH_FIRST; |
750 | 0 | const UHashElement* entry = nullptr; |
751 | 0 | while ((entry = m->nextElement(pos)) != nullptr) { |
752 | 0 | const UnicodeString* id = static_cast<const UnicodeString*>(entry->key.pointer); |
753 | 0 | ICUServiceFactory* f = static_cast<ICUServiceFactory*>(entry->value.pointer); |
754 | 0 | UnicodeString dname; |
755 | 0 | f->getDisplayName(*id, locale, dname); |
756 | 0 | if (dname.isBogus()) { |
757 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
758 | 0 | } else { |
759 | 0 | dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap |
760 | 0 | if (U_SUCCESS(status)) { |
761 | 0 | continue; |
762 | 0 | } |
763 | 0 | } |
764 | 0 | delete dnCache; |
765 | 0 | ncthis->dnCache = nullptr; |
766 | 0 | return result; |
767 | 0 | } |
768 | 0 | } |
769 | 0 | } |
770 | | |
771 | 0 | 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 | 0 | int32_t pos = UHASH_FIRST; |
777 | 0 | const UHashElement *entry = nullptr; |
778 | 0 | while ((entry = dnCache->cache.nextElement(pos)) != nullptr) { |
779 | 0 | const UnicodeString* id = static_cast<const UnicodeString*>(entry->value.pointer); |
780 | 0 | if (matchKey != nullptr && !matchKey->isFallbackOf(*id)) { |
781 | 0 | continue; |
782 | 0 | } |
783 | 0 | const UnicodeString* dn = static_cast<const UnicodeString*>(entry->key.pointer); |
784 | 0 | StringPair* sp = StringPair::create(*id, *dn, status); |
785 | 0 | result.adoptElement(sp, status); |
786 | 0 | if (U_FAILURE(status)) { |
787 | 0 | result.removeAllElements(); |
788 | 0 | break; |
789 | 0 | } |
790 | 0 | } |
791 | 0 | delete matchKey; |
792 | |
|
793 | 0 | return result; |
794 | 0 | } |
795 | | |
796 | | URegistryKey |
797 | | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) |
798 | 0 | { |
799 | 0 | return registerInstance(objToAdopt, id, true, status); |
800 | 0 | } |
801 | | |
802 | | URegistryKey |
803 | | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
804 | 0 | { |
805 | 0 | ICUServiceKey* key = createKey(&id, status); |
806 | 0 | if (key != nullptr) { |
807 | 0 | UnicodeString canonicalID; |
808 | 0 | key->canonicalID(canonicalID); |
809 | 0 | delete key; |
810 | |
|
811 | 0 | ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); |
812 | 0 | if (f != nullptr) { |
813 | 0 | return registerFactory(f, status); |
814 | 0 | } |
815 | 0 | } |
816 | 0 | delete objToAdopt; |
817 | 0 | return nullptr; |
818 | 0 | } |
819 | | |
820 | | ICUServiceFactory* |
821 | | ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
822 | 0 | { |
823 | 0 | if (U_SUCCESS(status)) { |
824 | 0 | if ((objToAdopt != nullptr) && (!id.isBogus())) { |
825 | 0 | return new SimpleFactory(objToAdopt, id, visible); |
826 | 0 | } |
827 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
828 | 0 | } |
829 | 0 | return nullptr; |
830 | 0 | } |
831 | | |
832 | | URegistryKey |
833 | | ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) |
834 | 0 | { |
835 | 0 | LocalPointer<ICUServiceFactory>lpFactoryToAdopt(factoryToAdopt); |
836 | 0 | if (U_FAILURE(status) || factoryToAdopt == nullptr) { |
837 | 0 | return nullptr; |
838 | 0 | } |
839 | 0 | { |
840 | 0 | Mutex mutex(&lock); |
841 | |
|
842 | 0 | if (factories == nullptr) { |
843 | 0 | LocalPointer<UVector> lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status); |
844 | 0 | if (U_FAILURE(status)) { |
845 | 0 | return nullptr; |
846 | 0 | } |
847 | 0 | factories = lpFactories.orphan(); |
848 | 0 | } |
849 | 0 | factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status); |
850 | 0 | if (U_SUCCESS(status)) { |
851 | 0 | clearCaches(); |
852 | 0 | } |
853 | 0 | } // Close of mutex lock block. |
854 | | |
855 | 0 | if (U_SUCCESS(status)) { |
856 | 0 | notifyChanged(); |
857 | 0 | return (URegistryKey)factoryToAdopt; |
858 | 0 | } else { |
859 | 0 | return nullptr; |
860 | 0 | } |
861 | 0 | } |
862 | | |
863 | | UBool |
864 | | ICUService::unregister(URegistryKey rkey, UErrorCode& status) |
865 | 0 | { |
866 | 0 | ICUServiceFactory *factory = (ICUServiceFactory*)rkey; |
867 | 0 | UBool result = false; |
868 | 0 | if (factory != nullptr && factories != nullptr) { |
869 | 0 | Mutex mutex(&lock); |
870 | |
|
871 | 0 | if (factories->removeElement(factory)) { |
872 | 0 | clearCaches(); |
873 | 0 | result = true; |
874 | 0 | } else { |
875 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
876 | 0 | delete factory; |
877 | 0 | } |
878 | 0 | } |
879 | 0 | if (result) { |
880 | 0 | notifyChanged(); |
881 | 0 | } |
882 | 0 | return result; |
883 | 0 | } |
884 | | |
885 | | void |
886 | | ICUService::reset() |
887 | 0 | { |
888 | 0 | { |
889 | 0 | Mutex mutex(&lock); |
890 | 0 | reInitializeFactories(); |
891 | 0 | clearCaches(); |
892 | 0 | } |
893 | 0 | notifyChanged(); |
894 | 0 | } |
895 | | |
896 | | void |
897 | | ICUService::reInitializeFactories() |
898 | 0 | { |
899 | 0 | if (factories != nullptr) { |
900 | 0 | factories->removeAllElements(); |
901 | 0 | } |
902 | 0 | } |
903 | | |
904 | | UBool |
905 | | ICUService::isDefault() const |
906 | 0 | { |
907 | 0 | return countFactories() == 0; |
908 | 0 | } |
909 | | |
910 | | ICUServiceKey* |
911 | | ICUService::createKey(const UnicodeString* id, UErrorCode& status) const |
912 | 0 | { |
913 | 0 | return (U_FAILURE(status) || id == nullptr) ? nullptr : new ICUServiceKey(*id); |
914 | 0 | } |
915 | | |
916 | | void |
917 | | ICUService::clearCaches() |
918 | 0 | { |
919 | | // callers synchronize before use |
920 | 0 | ++timestamp; |
921 | 0 | delete dnCache; |
922 | 0 | dnCache = nullptr; |
923 | 0 | delete idCache; |
924 | 0 | idCache = nullptr; |
925 | 0 | delete serviceCache; serviceCache = nullptr; |
926 | 0 | } |
927 | | |
928 | | void |
929 | | ICUService::clearServiceCache() |
930 | 0 | { |
931 | | // callers synchronize before use |
932 | 0 | delete serviceCache; serviceCache = nullptr; |
933 | 0 | } |
934 | | |
935 | | UBool |
936 | | ICUService::acceptsListener(const EventListener& l) const |
937 | 0 | { |
938 | 0 | return dynamic_cast<const ServiceListener*>(&l) != nullptr; |
939 | 0 | } |
940 | | |
941 | | void |
942 | | ICUService::notifyListener(EventListener& l) const |
943 | 0 | { |
944 | 0 | (static_cast<ServiceListener&>(l)).serviceChanged(*this); |
945 | 0 | } |
946 | | |
947 | | UnicodeString& |
948 | | ICUService::getName(UnicodeString& result) const |
949 | 0 | { |
950 | 0 | return result.append(name); |
951 | 0 | } |
952 | | |
953 | | int32_t |
954 | | ICUService::countFactories() const |
955 | 0 | { |
956 | 0 | return factories == nullptr ? 0 : factories->size(); |
957 | 0 | } |
958 | | |
959 | | int32_t |
960 | | ICUService::getTimestamp() const |
961 | 0 | { |
962 | 0 | return timestamp; |
963 | 0 | } |
964 | | |
965 | | U_NAMESPACE_END |
966 | | |
967 | | /* UCONFIG_NO_SERVICE */ |
968 | | #endif |