]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/locavailable.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / common / locavailable.cpp
index 5f06aff3a2dcc4b84d8338b77df492febb506da1..e8ec512e370992777ec493d15f95448684d3442e 100644 (file)
 *   that then do not depend on resource bundle code and res_index bundles.
 */
 
+#include "unicode/errorcode.h"
 #include "unicode/utypes.h"
 #include "unicode/locid.h"
 #include "unicode/uloc.h"
 #include "unicode/ures.h"
 #include "cmemory.h"
+#include "cstring.h"
 #include "ucln_cmn.h"
 #include "uassert.h"
 #include "umutex.h"
@@ -95,85 +97,174 @@ U_NAMESPACE_USE
 
 /* ### Constants **************************************************/
 
-/* These strings describe the resources we attempt to load from
- the locale ResourceBundle data file.*/
-static const char _kIndexLocaleName[] = "res_index";
-static const char _kIndexTag[]        = "InstalledLocales";
+namespace {
 
-static char** _installedLocales = NULL;
-static int32_t _installedLocalesCount = 0;
-static icu::UInitOnce _installedLocalesInitOnce;
+// Enough capacity for the two lists in the res_index.res file
+const char** gAvailableLocaleNames[2] = {};
+int32_t gAvailableLocaleCounts[2] = {};
+icu::UInitOnce ginstalledLocalesInitOnce = U_INITONCE_INITIALIZER;
 
-/* ### Get available **************************************************/
+class AvailableLocalesSink : public ResourceSink {
+  public:
+    void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE {
+        ResourceTable resIndexTable = value.getTable(status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) {
+            ULocAvailableType type;
+            if (uprv_strcmp(key, "InstalledLocales") == 0) {
+                type = ULOC_AVAILABLE_DEFAULT;
+            } else if (uprv_strcmp(key, "AliasLocales") == 0) {
+                type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
+            } else {
+                // CLDRVersion, etc.
+                continue;
+            }
+            ResourceTable availableLocalesTable = value.getTable(status);
+            if (U_FAILURE(status)) {
+                return;
+            }
+            gAvailableLocaleCounts[type] = availableLocalesTable.getSize();
+            gAvailableLocaleNames[type] = static_cast<const char**>(
+                uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*)));
+            if (gAvailableLocaleNames[type] == nullptr) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                return;
+            }
+            for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) {
+                gAvailableLocaleNames[type][j] = key;
+            }
+        }
+    }
+};
 
-static UBool U_CALLCONV uloc_cleanup(void) {
-    char ** temp;
+class AvailableLocalesStringEnumeration : public StringEnumeration {
+  public:
+    AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) {
+    }
+
+    const char* next(int32_t *resultLength, UErrorCode&) override {
+        ULocAvailableType actualType = fType;
+        int32_t actualIndex = fIndex++;
+
+        // If the "combined" list was requested, resolve that now
+        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
+            int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT];
+            if (actualIndex < defaultLocalesCount) {
+                actualType = ULOC_AVAILABLE_DEFAULT;
+            } else {
+                actualIndex -= defaultLocalesCount;
+                actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
+            }
+        }
 
-    if (_installedLocales) {
-        temp = _installedLocales;
-        _installedLocales = NULL;
+        // Return the requested string
+        int32_t count = gAvailableLocaleCounts[actualType];
+        const char* result;
+        if (actualIndex < count) {
+            result = gAvailableLocaleNames[actualType][actualIndex];
+            if (resultLength != nullptr) {
+                *resultLength = static_cast<int32_t>(uprv_strlen(result));
+            }
+        } else {
+            result = nullptr;
+            if (resultLength != nullptr) {
+                *resultLength = 0;
+            }
+        }
+        return result;
+    }
 
-        _installedLocalesCount = 0;
-        _installedLocalesInitOnce.reset();
+    void reset(UErrorCode&) override {
+        fIndex = 0;
+    }
 
-        uprv_free(temp);
+    int32_t count(UErrorCode&) const override {
+        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
+            return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]
+                + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES];
+        } else {
+            return gAvailableLocaleCounts[fType];
+        }
     }
+
+  private:
+    ULocAvailableType fType;
+    int32_t fIndex = 0;
+};
+
+/* ### Get available **************************************************/
+
+static UBool U_CALLCONV uloc_cleanup(void) {
+    for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) {
+        uprv_free(gAvailableLocaleNames[i]);
+        gAvailableLocaleNames[i] = nullptr;
+        gAvailableLocaleCounts[i] = 0;
+    }
+    ginstalledLocalesInitOnce.reset();
     return TRUE;
 }
 
 // Load Installed Locales. This function will be called exactly once
 //   via the initOnce mechanism.
 
-static void U_CALLCONV loadInstalledLocales() {
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t i = 0;
-    int32_t localeCount;
-    
-    U_ASSERT(_installedLocales == NULL);
-    U_ASSERT(_installedLocalesCount == 0);
-
-    _installedLocalesCount = 0;
+static void U_CALLCONV loadInstalledLocales(UErrorCode& status) {
+    ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup);
 
-    icu::LocalUResourceBundlePointer indexLocale(ures_openDirect(NULL, _kIndexLocaleName, &status));
-    icu::StackUResourceBundle installed;
-
-    ures_getByKey(indexLocale.getAlias(), _kIndexTag, installed.getAlias(), &status);
-
-    if(U_SUCCESS(status)) {
-        localeCount = ures_getSize(installed.getAlias());
-        _installedLocales = (char **) uprv_malloc(sizeof(char*) * (localeCount+1));
-        if (_installedLocales != NULL) {
-            ures_resetIterator(installed.getAlias());
-            while(ures_hasNext(installed.getAlias())) {
-                ures_getNextString(installed.getAlias(), NULL, (const char **)&_installedLocales[i++], &status);
-            }
-            _installedLocales[i] = NULL;
-            _installedLocalesCount = localeCount;
-            ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup);
-        }
-    }
+    icu::LocalUResourceBundlePointer rb(ures_openDirect(NULL, "res_index", &status));
+    AvailableLocalesSink sink;
+    ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status);
 }
 
-static void _load_installedLocales()
-{
-    umtx_initOnce(_installedLocalesInitOnce, &loadInstalledLocales);
+void _load_installedLocales(UErrorCode& status) {
+    umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status);
 }
 
+} // namespace
+
 U_CAPI const char* U_EXPORT2
-uloc_getAvailable(int32_t offset) 
-{
-    
-    _load_installedLocales();
-    
-    if (offset > _installedLocalesCount)
-        return NULL;
-    return _installedLocales[offset];
+uloc_getAvailable(int32_t offset) {
+    icu::ErrorCode status;
+    _load_installedLocales(status);
+    if (status.isFailure()) {
+        return nullptr;
+    }
+    if (offset > gAvailableLocaleCounts[0]) {
+        // *status = U_ILLEGAL_ARGUMENT_ERROR;
+        return nullptr;
+    }
+    return gAvailableLocaleNames[0][offset];
 }
 
 U_CAPI int32_t  U_EXPORT2
-uloc_countAvailable()
-{
-    _load_installedLocales();
-    return _installedLocalesCount;
+uloc_countAvailable() {
+    icu::ErrorCode status;
+    _load_installedLocales(status);
+    if (status.isFailure()) {
+        return 0;
+    }
+    return gAvailableLocaleCounts[0];
+}
+
+U_CAPI UEnumeration* U_EXPORT2
+uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) {
+    if (U_FAILURE(*status)) {
+        return nullptr;
+    }
+    if (type < 0 || type >= ULOC_AVAILABLE_COUNT) {
+        *status = U_ILLEGAL_ARGUMENT_ERROR;
+        return nullptr;
+    }
+    _load_installedLocales(*status);
+    if (U_FAILURE(*status)) {
+        return nullptr;
+    }
+    LocalPointer<AvailableLocalesStringEnumeration> result(
+        new AvailableLocalesStringEnumeration(type), *status);
+    if (U_FAILURE(*status)) {
+        return nullptr;
+    }
+    return uenum_openFromStringEnumeration(result.orphan(), status);
 }