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