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