]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/serv.cpp
ICU-8.11.tar.gz
[apple/icu.git] / icuSources / common / serv.cpp
diff --git a/icuSources/common/serv.cpp b/icuSources/common/serv.cpp
new file mode 100644 (file)
index 0000000..c00b9c8
--- /dev/null
@@ -0,0 +1,971 @@
+ /**
+ *******************************************************************************
+ * Copyright (C) 2001-2004, International Business Machines Corporation.       *
+ * All Rights Reserved.                                                        *
+ *******************************************************************************
+ */
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "serv.h"
+#include "umutex.h"
+
+#undef SERVICE_REFCOUNT
+
+// in case we use the refcount stuff
+
+U_NAMESPACE_BEGIN
+
+/*
+ ******************************************************************
+ */
+
+const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F;   /* '/' */
+
+ICUServiceKey::ICUServiceKey(const UnicodeString& id) 
+  : _id(id) {
+}
+
+ICUServiceKey::~ICUServiceKey() 
+{
+}
+
+const UnicodeString& 
+ICUServiceKey::getID() const 
+{
+  return _id;
+}
+
+UnicodeString& 
+ICUServiceKey::canonicalID(UnicodeString& result) const 
+{
+  return result.append(_id);
+}
+
+UnicodeString& 
+ICUServiceKey::currentID(UnicodeString& result) const 
+{
+  return canonicalID(result);
+}
+
+UnicodeString& 
+ICUServiceKey::currentDescriptor(UnicodeString& result) const 
+{
+  prefix(result);
+  result.append(PREFIX_DELIMITER);
+  return currentID(result);
+}
+
+UBool 
+ICUServiceKey::fallback() 
+{
+  return FALSE;
+}
+
+UBool 
+ICUServiceKey::isFallbackOf(const UnicodeString& id) const 
+{
+  return id == _id;
+}
+
+UnicodeString& 
+ICUServiceKey::prefix(UnicodeString& result) const 
+{
+  return result;
+}
+
+UnicodeString& 
+ICUServiceKey::parsePrefix(UnicodeString& result) 
+{
+  int32_t n = result.indexOf(PREFIX_DELIMITER);
+  if (n < 0) {
+    n = 0;
+  }
+  result.remove(n);
+  return result;
+}
+
+UnicodeString& 
+ICUServiceKey::parseSuffix(UnicodeString& result) 
+{
+  int32_t n = result.indexOf(PREFIX_DELIMITER);
+  if (n >= 0) {
+    result.remove(0, n+1);
+  }
+  return result;
+}
+
+#ifdef SERVICE_DEBUG
+UnicodeString& 
+ICUServiceKey::debug(UnicodeString& result) const 
+{
+  debugClass(result);
+  result.append(" id: ");
+  result.append(_id);
+  return result;
+}
+
+UnicodeString& 
+ICUServiceKey::debugClass(UnicodeString& result) const 
+{
+  return result.append("ICUServiceKey");
+}
+#endif
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
+
+/*
+ ******************************************************************
+ */
+
+SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) 
+  : _instance(instanceToAdopt), _id(id), _visible(visible)
+{
+}
+
+SimpleFactory::~SimpleFactory() 
+{
+  delete _instance;
+}
+
+UObject* 
+SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const 
+{
+  if (U_SUCCESS(status)) {
+    UnicodeString temp;
+    if (_id == key.currentID(temp)) {
+      return service->cloneInstance(_instance); 
+    }
+  }
+  return NULL;
+}
+
+void 
+SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const 
+{
+  if (_visible) {
+    result.put(_id, (void*)this, status); // cast away const
+  } else {
+    result.remove(_id);
+  }
+}
+
+UnicodeString& 
+SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const 
+{
+  if (_visible && _id == id) {
+    result = _id;
+  } else {
+    result.setToBogus();
+  }
+  return result;
+}
+
+#ifdef SERVICE_DEBUG
+UnicodeString& 
+SimpleFactory::debug(UnicodeString& toAppendTo) const 
+{
+  debugClass(toAppendTo);
+  toAppendTo.append(" id: ");
+  toAppendTo.append(_id);
+  toAppendTo.append(", visible: ");
+  toAppendTo.append(_visible ? "T" : "F");
+  return toAppendTo;
+}
+
+UnicodeString& 
+SimpleFactory::debugClass(UnicodeString& toAppendTo) const 
+{
+  return toAppendTo.append("SimpleFactory");
+}
+#endif
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
+
+/*
+ ******************************************************************
+ */
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
+
+/*
+ ******************************************************************
+ */
+
+ // Record the actual id for this service in the cache, so we can return it
+ // even if we succeed later with a different id.
+class CacheEntry : public UMemory {
+private:
+  int32_t refcount;
+
+public:
+  UnicodeString actualDescriptor;
+  UObject* service;
+
+  /**
+   * Releases a reference to the shared resource.
+   */
+  ~CacheEntry() {
+    delete service;
+  }
+
+  CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) 
+    : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
+  }
+
+  /**
+   * Instantiation creates an initial reference, so don't call this
+   * unless you're creating a new pointer to this.  Management of
+   * that pointer will have to know how to deal with refcounts.  
+   * Return true if the resource has not already been released.
+   */
+  CacheEntry* ref() {
+    ++refcount;
+    return this;
+  }
+
+  /**
+   * Destructions removes a reference, so don't call this unless
+   * you're removing pointer to this somewhere.  Management of that
+   * pointer will have to know how to deal with refcounts.  Once
+   * the refcount drops to zero, the resource is released.  Return
+   * false if the resouce has been released.
+   */
+  CacheEntry* unref() {
+    if ((--refcount) == 0) {
+      delete this;
+      return NULL;
+    }
+    return this;
+  }
+
+  /**
+   * Return TRUE if there is at least one reference to this and the
+   * resource has not been released.
+   */
+  UBool isShared() const {
+    return refcount > 1;
+  }
+};
+
+// UObjectDeleter for serviceCache
+U_CDECL_BEGIN
+static void U_CALLCONV
+cacheDeleter(void* obj) {
+  U_NAMESPACE_USE
+    ((CacheEntry*)obj)->unref();
+}
+
+/**
+ * Deleter for UObjects
+ */
+static void U_CALLCONV
+deleteUObject(void *obj) {
+  U_NAMESPACE_USE
+    delete (UObject*) obj;
+}
+U_CDECL_END
+
+/*
+ ******************************************************************
+ */
+
+class DNCache : public UMemory {
+public:
+  Hashtable cache;
+  const Locale locale;
+
+  DNCache(const Locale& _locale) 
+    : cache(), locale(_locale) 
+  {
+    // cache.setKeyDeleter(uhash_deleteUnicodeString);
+  }
+};
+
+
+/*
+ ******************************************************************
+ */
+
+StringPair* 
+StringPair::create(const UnicodeString& displayName, 
+                   const UnicodeString& id,
+                   UErrorCode& status)
+{
+  if (U_SUCCESS(status)) {
+    StringPair* sp = new StringPair(displayName, id);
+    if (sp == NULL || sp->isBogus()) {
+      status = U_MEMORY_ALLOCATION_ERROR;
+      delete sp;
+      return NULL;
+    }
+    return sp;
+  }
+  return NULL;
+}
+
+UBool 
+StringPair::isBogus() const {
+  return displayName.isBogus() || id.isBogus();
+}
+
+StringPair::StringPair(const UnicodeString& _displayName, 
+                       const UnicodeString& _id)
+  : displayName(_displayName)
+  , id(_id)
+{
+}
+
+U_CAPI void U_EXPORT2
+userv_deleteStringPair(void *obj) {
+  U_NAMESPACE_USE
+    delete (StringPair*) obj;
+}
+
+/*
+ ******************************************************************
+ */
+
+ICUService::ICUService()
+  : name()
+  , lock(0)
+  , timestamp(0)
+  , factories(NULL)
+  , serviceCache(NULL)
+  , idCache(NULL)
+  , dnCache(NULL)
+{
+  umtx_init(&lock);
+}
+
+ICUService::ICUService(const UnicodeString& newName) 
+  : name(newName)
+  , lock(0)
+  , timestamp(0)
+  , factories(NULL)
+  , serviceCache(NULL)
+  , idCache(NULL)
+  , dnCache(NULL) {
+  umtx_init(&lock);
+}
+
+ICUService::~ICUService()
+ {
+   {
+     Mutex mutex(&lock);
+     clearCaches();
+     delete factories;
+     factories = NULL;
+   }
+   umtx_destroy(&lock);
+}
+
+UObject* 
+ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const 
+{
+  return get(descriptor, NULL, status);
+}
+
+UObject* 
+ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const 
+{
+  UObject* result = NULL;
+    ICUServiceKey* key = createKey(&descriptor, status);
+    if (key) {
+      result = getKey(*key, actualReturn, status);
+      delete key;
+    }
+  return result;
+}
+
+UObject* 
+ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const 
+{
+  return getKey(key, NULL, status);
+}
+
+// this is a vector that subclasses of ICUService can override to further customize the result object
+// before returning it.  All other public get functions should call this one.
+
+UObject* 
+ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const 
+{
+  return getKey(key, actualReturn, NULL, status);
+}
+
+// make it possible to call reentrantly on systems that don't have reentrant mutexes.
+// we can use this simple approach since we know the situation where we're calling
+// reentrantly even without knowing the thread.
+class XMutex : public UMemory {
+public:
+  inline XMutex(UMTX *mutex, UBool reentering) 
+    : fMutex(mutex)
+    , fActive(!reentering) 
+  {
+    if (fActive) umtx_lock(fMutex);
+  }
+  inline ~XMutex() {
+    if (fActive) umtx_unlock(fMutex);
+  }
+
+private:
+  UMTX  *fMutex;
+  UBool fActive;
+};
+
+struct UVectorDeleter {
+  UVector* _obj;
+  UVectorDeleter() : _obj(NULL) {}
+  ~UVectorDeleter() { delete _obj; }
+};
+
+// called only by factories, treat as private
+UObject* 
+ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const 
+{
+  if (U_FAILURE(status)) {
+    return NULL;
+  }
+
+  if (isDefault()) {
+    return handleDefault(key, actualReturn, status);
+  }
+
+  ICUService* ncthis = (ICUService*)this; // cast away semantic const
+
+  CacheEntry* result = NULL;
+  {
+    // The factory list can't be modified until we're done, 
+    // otherwise we might update the cache with an invalid result.
+    // The cache has to stay in synch with the factory list.
+    // ICU doesn't have monitors so we can't use rw locks, so 
+    // we single-thread everything using this service, for now.
+    
+    // if factory is not null, we're calling from within the mutex,
+    // and since some unix machines don't have reentrant mutexes we
+    // need to make sure not to try to lock it again.
+    XMutex(&ncthis->lock, factory != NULL);
+
+    if (serviceCache == NULL) {
+      ncthis->serviceCache = new Hashtable(status);
+      if (U_FAILURE(status)) {
+        delete serviceCache;
+        return NULL;
+      }
+      serviceCache->setValueDeleter(cacheDeleter);
+    }
+
+    UnicodeString currentDescriptor;
+    UVectorDeleter cacheDescriptorList;
+    UBool putInCache = FALSE;
+
+    int32_t startIndex = 0;
+    int32_t limit = factories->size();
+    UBool cacheResult = TRUE;
+
+    if (factory != NULL) {
+      for (int32_t i = 0; i < limit; ++i) {
+        if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
+          startIndex = i + 1;
+          break;
+        }
+      }
+      if (startIndex == 0) {
+        // throw new InternalError("Factory " + factory + "not registered with service: " + this);
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return NULL;
+      }
+      cacheResult = FALSE;
+    }
+
+    do {
+      currentDescriptor.remove();
+      key.currentDescriptor(currentDescriptor);
+      result = (CacheEntry*)serviceCache->get(currentDescriptor);
+      if (result != NULL) {
+        break;
+      }
+
+      // first test of cache failed, so we'll have to update
+      // the cache if we eventually succeed-- that is, if we're 
+      // going to update the cache at all.
+      putInCache = TRUE;
+
+      int32_t index = startIndex;
+      while (index < limit) {
+        ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
+        UObject* service = f->create(key, this, status);
+        if (U_FAILURE(status)) {
+          delete service;
+          return NULL;
+        }
+        if (service != NULL) {
+          result = new CacheEntry(currentDescriptor, service);
+          if (result == NULL) {
+            delete service;
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return NULL;
+          }
+
+          goto outerEnd;
+        }
+      }
+
+      // prepare to load the cache with all additional ids that 
+      // will resolve to result, assuming we'll succeed.  We
+      // don't want to keep querying on an id that's going to
+      // fallback to the one that succeeded, we want to hit the
+      // cache the first time next goaround.
+      if (cacheDescriptorList._obj == NULL) {
+        cacheDescriptorList._obj = new UVector(uhash_deleteUnicodeString, NULL, 5, status);
+        if (U_FAILURE(status)) {
+          return NULL;
+        }
+      }
+      UnicodeString* idToCache = new UnicodeString(currentDescriptor);
+      if (idToCache == NULL || idToCache->isBogus()) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+      }
+
+      cacheDescriptorList._obj->addElement(idToCache, status);
+      if (U_FAILURE(status)) {
+        return NULL;
+      }
+    } while (key.fallback());
+  outerEnd:
+
+    if (result != NULL) {
+      if (putInCache && cacheResult) {
+        serviceCache->put(result->actualDescriptor, result, status);
+        if (U_FAILURE(status)) {
+          delete result;
+          return NULL;
+        }
+
+        if (cacheDescriptorList._obj != NULL) {
+          for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) {
+            UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i);
+            serviceCache->put(*desc, result, status);
+            if (U_FAILURE(status)) {
+              delete result;
+              return NULL;
+            }
+
+            result->ref();
+            cacheDescriptorList._obj->removeElementAt(i);
+          }
+        }
+      }
+
+      if (actualReturn != NULL) {
+        // strip null prefix
+        if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/)
+          actualReturn->remove();
+          actualReturn->append(result->actualDescriptor, 
+                               1, 
+                               result->actualDescriptor.length() - 1);
+        } else {
+          *actualReturn = result->actualDescriptor;
+        }
+
+        if (actualReturn->isBogus()) {
+          status = U_MEMORY_ALLOCATION_ERROR;
+      delete result;
+          return NULL;
+        }
+      }
+
+      UObject* service = cloneInstance(result->service);
+      if (putInCache && !cacheResult) {
+    delete result;
+      }
+      return service;
+    }
+  }
+
+  return handleDefault(key, actualReturn, status);
+}
+
+UObject* 
+ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const 
+{
+  return NULL;
+}
+  
+UVector& 
+ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
+  return getVisibleIDs(result, NULL, status);
+}
+
+UVector& 
+ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const 
+{
+  result.removeAllElements();
+
+  if (U_FAILURE(status)) {
+    return result;
+  }
+
+  ICUService * ncthis = (ICUService*)this; // cast away semantic const
+  {
+    Mutex mutex(&ncthis->lock);
+    const Hashtable* map = getVisibleIDMap(status);
+    if (map != NULL) {
+      ICUServiceKey* fallbackKey = createKey(matchID, status);
+
+      for (int32_t pos = -1;;) {
+        const UHashElement* e = map->nextElement(pos);
+        if (e == NULL) {
+          break;
+        }
+
+        const UnicodeString* id = (const UnicodeString*)e->key.pointer;
+        if (fallbackKey != NULL) {
+          if (!fallbackKey->isFallbackOf(*id)) {
+            continue;
+          }
+        }
+
+        UnicodeString* idClone = new UnicodeString(*id);
+        if (idClone == NULL || idClone->isBogus()) {
+          delete idClone;
+          status = U_MEMORY_ALLOCATION_ERROR;
+          break;
+        }
+        result.addElement(idClone, status);
+        if (U_FAILURE(status)) {
+          delete idClone;
+          break;
+        }
+      }
+      delete fallbackKey;
+    }
+  }
+  if (U_FAILURE(status)) {
+    result.removeAllElements();
+  }
+  return result;
+}
+
+const Hashtable* 
+ICUService::getVisibleIDMap(UErrorCode& status) const {
+  if (U_FAILURE(status)) return NULL;
+
+  // must only be called when lock is already held
+
+  ICUService* ncthis = (ICUService*)this; // cast away semantic const
+  if (idCache == NULL) {
+    ncthis->idCache = new Hashtable(status);
+    if (idCache == NULL) {
+      status = U_MEMORY_ALLOCATION_ERROR;
+    } else if (factories != NULL) {
+      for (int32_t pos = factories->size(); --pos >= 0;) {
+        ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
+        f->updateVisibleIDs(*idCache, status);
+      }
+      if (U_FAILURE(status)) {
+        delete idCache;
+        ncthis->idCache = NULL;
+      }
+    }
+  }
+
+  return idCache;
+}
+
+UnicodeString& 
+ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const 
+{
+  return getDisplayName(id, result, Locale::getDefault());
+}
+
+UnicodeString& 
+ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const 
+{
+  {
+    ICUService* ncthis = (ICUService*)this; // cast away semantic const
+    UErrorCode status = U_ZERO_ERROR;
+    Mutex mutex(&ncthis->lock);
+    const Hashtable* map = getVisibleIDMap(status);
+    if (map != NULL) {
+      ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
+      if (f != NULL) {
+        f->getDisplayName(id, locale, result);
+        return result;
+      }
+
+      // fallback
+      UErrorCode status = U_ZERO_ERROR;
+      ICUServiceKey* fallbackKey = createKey(&id, status);
+      while (fallbackKey->fallback()) {
+          UnicodeString us;
+          fallbackKey->currentID(us);
+          f = (ICUServiceFactory*)map->get(us);
+          if (f != NULL) {
+              f->getDisplayName(id, locale, result);
+              delete fallbackKey;
+              return result;
+          }
+      }
+      delete fallbackKey;
+    }
+  }
+  result.setToBogus();
+  return result;
+}
+
+UVector& 
+ICUService::getDisplayNames(UVector& result, UErrorCode& status) const 
+{
+  return getDisplayNames(result, Locale::getDefault(), NULL, status);
+}
+
+
+UVector& 
+ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const 
+{
+  return getDisplayNames(result, locale, NULL, status);
+}
+
+UVector& 
+ICUService::getDisplayNames(UVector& result, 
+                            const Locale& locale, 
+                            const UnicodeString* matchID, 
+                            UErrorCode& status) const 
+{
+  result.removeAllElements();
+  if (U_SUCCESS(status)) {
+    ICUService* ncthis = (ICUService*)this; // cast away semantic const
+    Mutex mutex(&ncthis->lock);
+
+    if (dnCache != NULL && dnCache->locale != locale) {
+      delete dnCache;
+      ncthis->dnCache = NULL;
+    }
+
+    if (dnCache == NULL) {
+      const Hashtable* m = getVisibleIDMap(status);
+      if (m != NULL) {
+        ncthis->dnCache = new DNCache(locale); 
+        if (dnCache == NULL) {
+          status = U_MEMORY_ALLOCATION_ERROR;
+          return result;
+        }
+
+        int32_t pos = 0;
+        const UHashElement* entry = NULL;
+        while ((entry = m->nextElement(pos)) != NULL) {
+          const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
+          ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
+          UnicodeString dname;
+          f->getDisplayName(*id, locale, dname);
+          if (dname.isBogus()) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+          } else {
+            dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
+            if (U_SUCCESS(status)) {
+              continue;
+            }
+          }
+          delete dnCache;
+          ncthis->dnCache = NULL;
+          return result;
+        }
+      }
+    }
+  }
+
+  ICUServiceKey* matchKey = createKey(matchID, status);
+  int32_t pos = 0;
+  const UHashElement *entry = NULL;
+  while ((entry = dnCache->cache.nextElement(pos)) != NULL) {
+    const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
+    if (matchKey != NULL && !matchKey->isFallbackOf(*id)) {
+      continue;
+    }
+    const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
+    StringPair* sp = StringPair::create(*id, *dn, status);
+    result.addElement(sp, status);
+    if (U_FAILURE(status)) {
+      result.removeAllElements();
+      break;
+    }
+  }
+  delete matchKey;
+
+  return result;
+}
+
+URegistryKey
+ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) 
+{
+  return registerInstance(objToAdopt, id, TRUE, status);
+}
+
+URegistryKey
+ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) 
+{
+  ICUServiceKey* key = createKey(&id, status);
+  if (key != NULL) {
+    UnicodeString canonicalID;
+    key->canonicalID(canonicalID);
+    delete key;
+
+    ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
+    if (f != NULL) {
+      return registerFactory(f, status);
+    }
+  }
+  delete objToAdopt;
+  return NULL;
+}
+
+ICUServiceFactory* 
+ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
+{
+  if (U_SUCCESS(status)) {
+    if ((objToAdopt != NULL) && (!id.isBogus())) {
+      return new SimpleFactory(objToAdopt, id, visible);
+    }
+    status = U_ILLEGAL_ARGUMENT_ERROR;
+  }
+  return NULL;
+}
+
+URegistryKey
+ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) 
+{
+  if (U_SUCCESS(status) && factoryToAdopt != NULL) {
+    Mutex mutex(&lock);
+
+    if (factories == NULL) {
+      factories = new UVector(deleteUObject, NULL, status);
+      if (U_FAILURE(status)) {
+        delete factories;
+        return NULL;
+      }
+    }
+    factories->insertElementAt(factoryToAdopt, 0, status);
+    if (U_SUCCESS(status)) {
+      clearCaches();
+    } else {
+      delete factoryToAdopt;
+      factoryToAdopt = NULL;
+    }
+  }
+
+  if (factoryToAdopt != NULL) {
+      notifyChanged();
+  }
+
+  return (URegistryKey)factoryToAdopt;
+}
+
+UBool 
+ICUService::unregister(URegistryKey rkey, UErrorCode& status) 
+{
+  ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
+  UBool result = FALSE;
+  if (factory != NULL && factories != NULL) {
+    Mutex mutex(&lock);
+
+    if (factories->removeElement(factory)) {
+      clearCaches();
+      result = TRUE;
+    } else {
+      status = U_ILLEGAL_ARGUMENT_ERROR;
+      delete factory;
+    }
+  }
+  if (result) {
+    notifyChanged();
+  }
+  return result;
+}
+
+void 
+ICUService::reset() 
+{
+  {
+    Mutex mutex(&lock);
+    reInitializeFactories();
+    clearCaches();
+  }
+  notifyChanged();
+}
+
+void 
+ICUService::reInitializeFactories() 
+{
+  if (factories != NULL) {
+    factories->removeAllElements();
+  }
+}
+
+UBool 
+ICUService::isDefault() const 
+{
+  return countFactories() == 0;
+}
+
+ICUServiceKey* 
+ICUService::createKey(const UnicodeString* id, UErrorCode& status) const 
+{
+  return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
+}
+
+void 
+ICUService::clearCaches() 
+{
+  // callers synchronize before use
+  ++timestamp;
+  delete dnCache; dnCache = NULL;
+  delete idCache; idCache = NULL;
+  delete serviceCache; serviceCache = NULL;
+}
+
+void 
+ICUService::clearServiceCache() 
+{
+  // callers synchronize before use
+  delete serviceCache; serviceCache = NULL;
+}
+
+UBool 
+ICUService::acceptsListener(const EventListener& l) const 
+{
+  return l.getDynamicClassID() == ServiceListener::getStaticClassID();
+}
+
+void 
+ICUService::notifyListener(EventListener& l) const 
+{
+  ((ServiceListener&)l).serviceChanged(*this);
+}
+
+UnicodeString&
+ICUService::getName(UnicodeString& result) const 
+{
+  return result.append(name);
+}
+
+int32_t 
+ICUService::countFactories() const 
+{
+  return factories == NULL ? 0 : factories->size();
+}
+
+int32_t
+ICUService::getTimestamp() const
+{
+  return timestamp;
+}
+
+U_NAMESPACE_END
+
+/* UCONFIG_NO_SERVICE */
+#endif