Coverage Report

Created: 2025-06-13 06:34

/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