]> granicus.if.org Git - icu/commitdiff
ICU-20627 Adding getAvailableLocalesByType, ICU4C and ICU4J.
authorShane Carr <shane@unicode.org>
Thu, 15 Aug 2019 01:56:22 +0000 (01:56 +0000)
committerShane F. Carr <shane@unicode.org>
Thu, 15 Aug 2019 03:45:27 +0000 (20:45 -0700)
14 files changed:
icu4c/source/common/locavailable.cpp
icu4c/source/common/unicode/uloc.h
icu4c/source/python/icutools/databuilder/request_types.py
icu4c/source/test/cintltst/cintltst.c
icu4c/source/test/cintltst/cintltst.h
icu4c/source/test/cintltst/cloctst.c
icu4c/source/test/cintltst/cloctst.h
icu4c/source/test/depstest/dependencies.txt
icu4j/main/classes/collate/src/com/ibm/icu/text/Collator.java
icu4j/main/classes/collate/src/com/ibm/icu/text/CollatorServiceShim.java
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java
icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java
icu4j/main/shared/data/icudata.jar
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java

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);
 }
 
index 23a54a9981ca9619fa0560245008ab0aa15722dd..c45edad9e0942be1ae8fe0e3aa9bb1df58a95d73 100644 (file)
@@ -742,12 +742,18 @@ uloc_getDisplayName(const char* localeID,
 
 
 /**
- * Gets the specified locale from a list of all available locales.  
- * The return value is a pointer to an item of 
- * a locale name array.  Both this array and the pointers
- * it contains are owned by ICU and should not be deleted or written through
- * by the caller.  The locale name is terminated by a null pointer.
- * @param n the specific locale name index of the available locale list
+ * Gets the specified locale from a list of available locales.
+ *
+ * This method corresponds to uloc_openAvailableByType called with the
+ * ULOC_AVAILABLE_DEFAULT type argument.
+ *
+ * The return value is a pointer to an item of a locale name array. Both this
+ * array and the pointers it contains are owned by ICU and should not be
+ * deleted or written through by the caller. The locale name is terminated by
+ * a null pointer.
+ *
+ * @param n the specific locale name index of the available locale list;
+ *     should not exceed the number returned by uloc_countAvailable.
  * @return a specified locale name of all available locales
  * @stable ICU 2.0
  */
@@ -762,6 +768,72 @@ uloc_getAvailable(int32_t n);
  */
 U_STABLE int32_t U_EXPORT2 uloc_countAvailable(void);
 
+#ifndef U_HIDE_DRAFT_API
+
+/**
+ * Types for uloc_getAvailableByType and uloc_countAvailableByType.
+ *
+ * @draft ICU 65
+ */
+typedef enum ULocAvailableType {
+  /**
+   * Locales that return data when passed to ICU APIs,
+   * but not including legacy or alias locales.
+   *
+   * @draft ICU 65
+   */
+  ULOC_AVAILABLE_DEFAULT,
+
+  /**
+   * Legacy or alias locales that return data when passed to ICU APIs.
+   * Examples of supported legacy or alias locales:
+   *
+   * - iw (alias to he)
+   * - mo (alias to ro)
+   * - zh_CN (alias to zh_Hans_CN)
+   * - sr_BA (alias to sr_Cyrl_BA)
+   * - ars (alias to ar_SA)
+   *
+   * The locales in this set are disjoint from the ones in
+   * ULOC_AVAILABLE_DEFAULT. To get both sets at the same time, use
+   * ULOC_AVAILABLE_WITH_LEGACY_ALIASES.
+   *
+   * @draft ICU 65
+   */
+  ULOC_AVAILABLE_ONLY_LEGACY_ALIASES,
+
+  /**
+   * The union of the locales in ULOC_AVAILABLE_DEFAULT and
+   * ULOC_AVAILABLE_ONLY_LEGACY_ALIAS.
+   *
+   * @draft ICU 65
+   */
+  ULOC_AVAILABLE_WITH_LEGACY_ALIASES,
+
+#ifndef U_HIDE_INTERNAL_API
+  /**
+   * @internal
+   */
+  ULOC_AVAILABLE_COUNT
+#endif
+} ULocAvailableType;
+
+/**
+ * Gets a list of available locales according to the type argument, allowing
+ * the user to access different sets of supported locales in ICU.
+ *
+ * The returned UEnumeration must be closed by the caller.
+ *
+ * @param type Type choice from ULocAvailableType.
+ * @param status Set if an error occurred.
+ * @return a UEnumeration owned by the caller, or nullptr on failure.
+ * @draft ICU 65
+ */
+U_DRAFT UEnumeration* U_EXPORT2
+uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status);
+
+#endif // U_HIDE_DRAFT_API
+
 /**
  *
  * Gets a list of all available 2-letter language codes defined in ISO 639,
index d7ffd799d19cd9ee84411f905faa848ace380337..aa70f8d9185b6683fcf2e1f8eff1c709ffedecf4 100644 (file)
@@ -340,10 +340,10 @@ class IndexRequest(AbstractRequest):
         return ("// Warning this file is automatically generated\n"
                 "{INDEX_NAME}:table(nofallback) {{\n"
                 "{FORMATTED_VERSION}"
-                "    InstalledLocales {{\n"
+                "    InstalledLocales:table {{\n"
                 "{FORMATTED_INSTALLED_LOCALES}\n"
                 "    }}\n"
-                "    AliasLocales {{\n"
+                "    AliasLocales:table {{\n"
                 "{FORMATTED_ALIAS_LOCALES}\n"
                 "    }}\n"
                 "}}").format(
index aaa187f6bdb14c22c5fb68c17e677f8d5c9fb8c3..498361392e1c6839ce6805f37bef066a9572f0ea 100644 (file)
@@ -743,4 +743,18 @@ U_CFUNC UBool assertIntEquals(const char* message, int64_t expected, int64_t act
     return TRUE;
 }
 
+U_CFUNC UBool assertPtrEquals(const char* message, const void* expected, const void* actual) {
+    if (expected != actual) {
+        log_err("FAIL: %s; got 0x%llx; expected 0x%llx\n",
+                message, actual, expected);
+        return FALSE;
+    }
+#ifdef VERBOSE_ASSERTIONS
+    else {
+        log_verbose("Ok: %s; got 0x%llx\n", message, actual);
+    }
+#endif
+    return TRUE;
+}
+
 #endif
index edb60eb58e8dd04ce82ba43b7053c1cd5e65ba9f..f66e4c6c07eeae7d83b5f27fdeb95fd1c1bbe534 100644 (file)
@@ -147,6 +147,12 @@ U_CFUNC UBool assertUEquals(const char* msg, const UChar* expectedString,
  */
 U_CFUNC UBool assertIntEquals(const char* msg, int64_t expected, int64_t actual);
 
+/**
+ * Assert that the addresses of the two pointers are the same, returning
+ * TRUE if they are equal.
+ */
+U_CFUNC UBool assertPtrEquals(const char* msg, const void* expected, const void* actual);
+
 /*
  * note - isICUVersionBefore and isICUVersionAtLeast have been removed.
  * use log_knownIssue() instead.
index 6c247e1f0328262119545849f2bc5a6989d10386..b53fe7c26b5127a77e887c6dd1833ee02083cea4 100644 (file)
@@ -218,6 +218,7 @@ void addLocaleTest(TestNode** root)
     TESTCASE(TestSimpleResourceInfo);
     TESTCASE(TestDisplayNames);
     TESTCASE(TestGetAvailableLocales);
+    TESTCASE(TestGetAvailableLocalesByType);
     TESTCASE(TestDataDirectory);
 #if !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION
     TESTCASE(TestISOFunctions);
@@ -834,6 +835,77 @@ static void TestGetAvailableLocales()
     }
 }
 
+static void TestGetAvailableLocalesByType() {
+    UErrorCode status = U_ZERO_ERROR;
+
+    UEnumeration* uenum = uloc_openAvailableByType(ULOC_AVAILABLE_DEFAULT, &status);
+    assertSuccess("Constructing the UEnumeration", &status);
+
+    assertIntEquals("countAvailable() should be same in old and new methods",
+        uloc_countAvailable(),
+        uenum_count(uenum, &status));
+
+    for (int32_t i = 0; i < uloc_countAvailable(); i++) {
+        const char* old = uloc_getAvailable(i);
+        int32_t len = 0;
+        const char* new = uenum_next(uenum, &len, &status);
+        assertEquals("Old and new strings should equal", old, new);
+        assertIntEquals("String length should be correct", uprv_strlen(old), len);
+    }
+    assertPtrEquals("Should get nullptr on the last string",
+        NULL, uenum_next(uenum, NULL, &status));
+
+    uenum_close(uenum);
+
+    uenum = uloc_openAvailableByType(ULOC_AVAILABLE_ONLY_LEGACY_ALIASES, &status);
+    UBool found_he = FALSE;
+    UBool found_iw = FALSE;
+    const char* loc;
+    while ((loc = uenum_next(uenum, NULL, &status))) {
+        if (uprv_strcmp("he", loc) == 0) {
+            found_he = TRUE;
+        }
+        if (uprv_strcmp("iw", loc) == 0) {
+            found_iw = TRUE;
+        }
+    }
+    assertTrue("Should NOT have found he amongst the legacy/alias locales", !found_he);
+    assertTrue("Should have found iw amongst the legacy/alias locales", found_iw);
+    uenum_close(uenum);
+
+    uenum = uloc_openAvailableByType(ULOC_AVAILABLE_WITH_LEGACY_ALIASES, &status);
+    found_he = FALSE;
+    found_iw = FALSE;
+    const UChar* uloc; // test the UChar conversion
+    int32_t count = 0;
+    while ((uloc = uenum_unext(uenum, NULL, &status))) {
+        if (u_strcmp(u"iw", uloc) == 0) {
+            found_iw = TRUE;
+        }
+        if (u_strcmp(u"he", uloc) == 0) {
+            found_he = TRUE;
+        }
+        count++;
+    }
+    assertTrue("Should have found he amongst all locales", found_he);
+    assertTrue("Should have found iw amongst all locales", found_iw);
+    assertIntEquals("Should return as many strings as claimed",
+        count, uenum_count(uenum, &status));
+
+    // Reset the enumeration and it should still work
+    uenum_reset(uenum, &status);
+    count = 0;
+    while ((loc = uenum_next(uenum, NULL, &status))) {
+        count++;
+    }
+    assertIntEquals("After reset, should return as many strings as claimed",
+        count, uenum_count(uenum, &status));
+
+    uenum_close(uenum);
+
+    assertSuccess("No errors should have occurred", &status);
+}
+
 /* test for u_getDataDirectory, u_setDataDirectory, uloc_getISO3Language */
 static void TestDataDirectory()
 {
index a88a49cf8d866a70d183f00207ad6b4494ad1daf..be61de10ba8c7da52ee61467b6a2217ed2652995 100644 (file)
@@ -36,7 +36,8 @@ static  void TestDisplayNames(void);
 /**
  * Test getAvailableLocales
  **/
- static  void TestGetAvailableLocales(void);
+static void TestGetAvailableLocales(void);
+static void TestGetAvailableLocalesByType(void);
 /**
  * Test functions to set and access a custom data directory
  **/
index dcb000ae4fa3c5ea3a414f646160dc2d8f285b46..33c9d1a7f5d44d3ed18403522aef5ba00271e3b3 100644 (file)
@@ -639,6 +639,7 @@ group: resourcebundle
     sort stringenumeration uhash uvector
     uscript_props propname
     bytesinkutil
+    errorcode
 
 group: localebuilder
     localebuilder.o
index fdbbf36f370b1150c791c00c96f31580bf3fc172..c2da6c525684f73339f3fc327504bff3c1ecd21f 100644 (file)
@@ -933,7 +933,7 @@ public abstract class Collator implements Comparator<Object>, Freezable<Collator
     public static final ULocale[] getAvailableULocales() {
         if (shim == null) {
             return ICUResourceBundle.getAvailableULocales(
-                ICUData.ICU_COLLATION_BASE_NAME, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
+                    ICUData.ICU_COLLATION_BASE_NAME, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
         }
         return shim.getAvailableULocales();
     }
index 53403a34f576515e64f0ca2b65caffe45f0592f8..6ae0e4f6ee1aad8e6ccb753e3a72d3d912b454e9 100644 (file)
@@ -179,7 +179,7 @@ final class CollatorServiceShim extends Collator.ServiceShim {
 
     // Ported from C++ Collator::makeInstance().
     private static final Collator makeInstance(ULocale desiredLocale) {
-        Output<ULocale> validLocale = new Output<ULocale>(ULocale.ROOT);
+        Output<ULocale> validLocale = new Output<>(ULocale.ROOT);
         CollationTailoring t =
             CollationLoader.loadTailoring(desiredLocale, validLocale);
         return new RuleBasedCollator(t, validLocale.value);
index 6a63d0e5bfeb85358ef14dd77f1bddbbf84aba24..74521e70a5a8f9a54983b971af8b235cd3d91b5d 100644 (file)
@@ -16,6 +16,7 @@ import java.io.InputStreamReader;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -132,7 +133,8 @@ public  class ICUResourceBundle extends UResourceBundle {
         r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
         if (isAvailable != null) {
             isAvailable[0] = false;
-            ULocale[] availableULocales = getAvailEntry(baseName, loader).getULocaleList();
+            ULocale[] availableULocales = getAvailEntry(baseName, loader)
+                    .getULocaleList(ULocale.AvailableType.DEFAULT);
             for (int i = 0; i < availableULocales.length; i++) {
                 if (parent.equals(availableULocales[i])) {
                     isAvailable[0] = true;
@@ -249,7 +251,8 @@ public  class ICUResourceBundle extends UResourceBundle {
      */
     public static final String[] getKeywordValues(String baseName, String keyword) {
         Set<String> keywords = new HashSet<>();
-        ULocale locales[] = getAvailEntry(baseName, ICU_DATA_CLASS_LOADER).getULocaleList();
+        ULocale locales[] = getAvailEntry(baseName, ICU_DATA_CLASS_LOADER)
+                .getULocaleList(ULocale.AvailableType.DEFAULT);
         int i;
 
         for (i = 0; i < locales.length; i++) {
@@ -493,11 +496,12 @@ public  class ICUResourceBundle extends UResourceBundle {
     }
 
     /**
-     * Get the set of Locales installed in the specified bundles.
+     * Get the set of Locales installed in the specified bundles, for the specified type.
      * @return the list of available locales
      */
-    public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader) {
-        return getAvailEntry(baseName, loader).getULocaleList();
+    public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader,
+            ULocale.AvailableType type) {
+        return getAvailEntry(baseName, loader).getULocaleList(type);
     }
 
     /**
@@ -505,23 +509,56 @@ public  class ICUResourceBundle extends UResourceBundle {
      * @return the list of available locales
      */
     public static final ULocale[] getAvailableULocales() {
-        return getAvailableULocales(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
+        return getAvailableULocales(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER, ULocale.AvailableType.DEFAULT);
+    }
+
+    /**
+     * Get the set of ULocales installed the base bundle, for the specified type.
+     * @return the list of available locales for the specified type
+     */
+    public static final ULocale[] getAvailableULocales(ULocale.AvailableType type) {
+        return getAvailableULocales(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER, type);
     }
 
     /**
      * Get the set of Locales installed in the specified bundles.
      * @return the list of available locales
      */
-    public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader) {
-        return getAvailEntry(baseName, loader).getLocaleList();
+    public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader) {
+        return getAvailableULocales(baseName, loader, ULocale.AvailableType.DEFAULT);
+    }
+
+    /**
+     * Get the set of Locales installed in the specified bundles, for the specified type.
+     * @return the list of available locales
+     */
+    public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader,
+            ULocale.AvailableType type) {
+        return getAvailEntry(baseName, loader).getLocaleList(type);
     }
 
-   /**
-     * Get the set of Locales installed the base bundle.
+    /**
+     * Get the set of ULocales installed the base bundle.
      * @return the list of available locales
      */
     public static final Locale[] getAvailableLocales() {
-        return getAvailEntry(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER).getLocaleList();
+        return getAvailableLocales(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER, ULocale.AvailableType.DEFAULT);
+    }
+
+    /**
+     * Get the set of Locales installed the base bundle, for the specified type.
+     * @return the list of available locales
+     */
+    public static final Locale[] getAvailableLocales(ULocale.AvailableType type) {
+        return getAvailableLocales(ICUData.ICU_BASE_NAME, ICU_DATA_CLASS_LOADER, type);
+    }
+
+    /**
+     * Get the set of Locales installed in the specified bundles.
+     * @return the list of available locales
+     */
+    public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader) {
+        return getAvailableLocales(baseName, loader, ULocale.AvailableType.DEFAULT);
     }
 
     /**
@@ -569,31 +606,50 @@ public  class ICUResourceBundle extends UResourceBundle {
     // Flag for enabling/disabling debugging code
     private static final boolean DEBUG = ICUDebug.enabled("localedata");
 
-    private static final ULocale[] createULocaleList(String baseName,
-            ClassLoader root) {
+    private static final class AvailableLocalesSink extends UResource.Sink {
+
+        EnumMap<ULocale.AvailableType, ULocale[]> output;
+
+        public AvailableLocalesSink(EnumMap<ULocale.AvailableType, ULocale[]> output) {
+            this.output = output;
+        }
+
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            UResource.Table resIndexTable = value.getTable();
+            for (int i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) {
+                ULocale.AvailableType type;
+                if (key.contentEquals("InstalledLocales")) {
+                    type = ULocale.AvailableType.DEFAULT;
+                } else if (key.contentEquals("AliasLocales")) {
+                    type = ULocale.AvailableType.ONLY_LEGACY_ALIASES;
+                } else {
+                    // CLDRVersion, etc.
+                    continue;
+                }
+                UResource.Table availableLocalesTable = value.getTable();
+                ULocale[] locales = new ULocale[availableLocalesTable.getSize()];
+                for (int j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) {
+                    locales[j] = new ULocale(key.toString());
+                }
+                output.put(type, locales);
+            }
+        }
+    }
+
+    private static final EnumMap<ULocale.AvailableType, ULocale[]> createULocaleList(
+            String baseName, ClassLoader root) {
         // the canned list is a subset of all the available .res files, the idea
         // is we don't export them
         // all. gotta be a better way to do this, since to add a locale you have
         // to update this list,
         // and it's embedded in our binary resources.
-        ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle.instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
+        ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
 
-        bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);
-        int length = bundle.getSize();
-        int i = 0;
-        ULocale[] locales = new ULocale[length];
-        UResourceBundleIterator iter = bundle.getIterator();
-        iter.reset();
-        while (iter.hasNext()) {
-            String locstr = iter.next().getKey();
-            if (locstr.equals("root")) {
-                locales[i++] = ULocale.ROOT;
-            } else {
-                locales[i++] = new ULocale(locstr);
-            }
-        }
-        bundle = null;
-        return locales;
+        EnumMap<ULocale.AvailableType, ULocale[]> result = new EnumMap<>(ULocale.AvailableType.class);
+        AvailableLocalesSink sink = new AvailableLocalesSink(result);
+        rb.getAllItemsWithFallback("", sink);
+        return result;
     }
 
     // Same as createULocaleList() but catches the MissingResourceException
@@ -739,7 +795,7 @@ public  class ICUResourceBundle extends UResourceBundle {
     private static final class AvailEntry {
         private String prefix;
         private ClassLoader loader;
-        private volatile ULocale[] ulocales;
+        private volatile EnumMap<ULocale.AvailableType, ULocale[]> ulocales;
         private volatile Locale[] locales;
         private volatile Set<String> nameSet;
         private volatile Set<String> fullNameSet;
@@ -749,7 +805,9 @@ public  class ICUResourceBundle extends UResourceBundle {
             this.loader = loader;
         }
 
-        ULocale[] getULocaleList() {
+        ULocale[] getULocaleList(ULocale.AvailableType type) {
+            // Direct data is available for DEFAULT and ONLY_LEGACY_ALIASES
+            assert type != ULocale.AvailableType.WITH_LEGACY_ALIASES;
             if (ulocales == null) {
                 synchronized(this) {
                     if (ulocales == null) {
@@ -757,14 +815,14 @@ public  class ICUResourceBundle extends UResourceBundle {
                     }
                 }
             }
-            return ulocales;
+            return ulocales.get(type);
         }
-        Locale[] getLocaleList() {
+        Locale[] getLocaleList(ULocale.AvailableType type) {
             if (locales == null) {
-                getULocaleList();
+                getULocaleList(type);
                 synchronized(this) {
                     if (locales == null) {
-                        locales = ICUResourceBundle.getLocaleList(ulocales);
+                        locales = ICUResourceBundle.getLocaleList(ulocales.get(type));
                     }
                 }
             }
index 37d72bf0f4e31a0a616f8d51c560f185ac380bde..a8d8f7f115f65794178f8da72e1303eff86fde30 100644 (file)
@@ -13,6 +13,10 @@ import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -121,6 +125,52 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
         }
     };
 
+    /**
+     * Types for {@link ULocale#getAvailableLocalesByType}
+     *
+     * @draft ICU 65
+     * @provisional This API might change or be removed in a future release.
+     */
+    public static enum AvailableType {
+        /**
+         * Locales that return data when passed to ICU APIs,
+         * but not including legacy or alias locales.
+         *
+         * @draft ICU 65
+         * @provisional This API might change or be removed in a future release.
+         */
+        DEFAULT,
+
+        /**
+         * Legacy or alias locales that return data when passed to ICU APIs.
+         * Examples of supported legacy or alias locales:
+         *
+         * <ul>
+         * <li>iw (alias to he)
+         * <li>mo (alias to ro)
+         * <li>zh_CN (alias to zh_Hans_CN)
+         * <li>sr_BA (alias to sr_Cyrl_BA)
+         * <li>ars (alias to ar_SA)
+         * </ul>
+         *
+         * The locales in this set are disjoint from the ones in
+         * DEFAULT. To get both sets at the same time, use
+         * WITH_LEGACY_ALIASES.
+         *
+         * @draft ICU 65
+         * @provisional This API might change or be removed in a future release.
+         */
+        ONLY_LEGACY_ALIASES,
+
+        /**
+         * The union of the locales in DEFAULT and ONLY_LEGACY_ALIASES.
+         *
+         * @draft ICU 65
+         * @provisional This API might change or be removed in a future release.
+         */
+        WITH_LEGACY_ALIASES,
+    }
+
     /**
      * Useful constant for language.
      * @stable ICU 3.0
@@ -786,11 +836,38 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
 
     /**
      * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
-     * not <code>Locale</code>.  Returns a list of all installed locales.
+     * not <code>Locale</code>.
+     *
+     * <p>Returns a list of all installed locales. This is equivalent to calling
+     * {@link #getAvailableLocalesByType} with AvialableType.DEFAULT.
+     *
      * @stable ICU 3.0
      */
     public static ULocale[] getAvailableLocales() {
-        return ICUResourceBundle.getAvailableULocales();
+        return ICUResourceBundle.getAvailableULocales().clone();
+    }
+
+    /**
+     * Returns a list of all installed locales according to the specified type.
+     *
+     * @draft ICU 65
+     * @provisional This API might change or be removed in a future release.
+     */
+    public static Collection<ULocale> getAvailableLocalesByType(AvailableType type) {
+        if (type == null) {
+            throw new IllegalArgumentException();
+        }
+        List<ULocale> result;
+        if (type == ULocale.AvailableType.WITH_LEGACY_ALIASES) {
+            result = new ArrayList<>();
+            Collections.addAll(result,
+                    ICUResourceBundle.getAvailableULocales(ULocale.AvailableType.DEFAULT));
+            Collections.addAll(result,
+                    ICUResourceBundle.getAvailableULocales(ULocale.AvailableType.ONLY_LEGACY_ALIASES));
+        } else {
+            result = Arrays.asList(ICUResourceBundle.getAvailableULocales(type));
+        }
+        return Collections.unmodifiableList(result);
     }
 
     /**
index d1d89d2711bca3c7a7732b9f5c09eb8c3dcb9e59..51a042e529c37187a813e988111ae67f19d5bfd4 100644 (file)
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:5c8773434e9708bca02ad11319c35e01f29f62748851a38ae89de1334c279cca
-size 12842785
+oid sha256:43a8fe03049ca2fc11189c20cdc0baa3248a983d9885fe44f7946aca7082979a
+size 12842784
index e71a8332cf940cfc8e842e3e60d45006a873141e..fc2dc4f98cb9e053f4efb9b115b3b0bebce4c5a7 100644 (file)
@@ -15,6 +15,7 @@ package com.ibm.icu.dev.test.util;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -1024,6 +1025,49 @@ public class ULocaleTest extends TestFmwk {
         }
     }
 
+    @Test
+    public void TestGetAvailableByType() {
+        assertEquals("countAvailable() should be same in old and new methods",
+            ULocale.getAvailableLocales().length,
+            ULocale.getAvailableLocalesByType(ULocale.AvailableType.DEFAULT).size());
+
+        assertEquals("getAvailable() should return same in old and new methods",
+            Arrays.asList(ULocale.getAvailableLocales()),
+            ULocale.getAvailableLocalesByType(ULocale.AvailableType.DEFAULT));
+
+        Collection<ULocale> legacyLocales = ULocale
+                .getAvailableLocalesByType(ULocale.AvailableType.ONLY_LEGACY_ALIASES);
+        assertTrue("getAvailable() legacy/alias should return nonempty", legacyLocales.size() > 0);
+
+        boolean found_he = false;
+        boolean found_iw = false;
+        for (ULocale loc : legacyLocales) {
+            if (loc.getName().equals("he")) {
+                found_he = true;
+            }
+            if (loc.getName().equals("iw")) {
+                found_iw = true;
+            }
+        }
+        assertFalse("Should NOT have found he amongst the legacy/alias locales", found_he);
+        assertTrue("Should have found iw amongst the legacy/alias locales", found_iw);
+
+        Collection<ULocale> allLocales = ULocale
+                .getAvailableLocalesByType(ULocale.AvailableType.WITH_LEGACY_ALIASES);
+        found_he = false;
+        found_iw = false;
+        for (ULocale loc : allLocales) {
+            if (loc.getName().equals("he")) {
+                found_he = true;
+            }
+            if (loc.getName().equals("iw")) {
+                found_iw = true;
+            }
+        }
+        assertTrue("Should have found he amongst the legacy/alias locales", found_he);
+        assertTrue("Should have found iw amongst the legacy/alias locales", found_iw);
+    }
+
     @Test
     public void TestDisplayNames() {
         // consistency check, also check that all data is available
@@ -4154,7 +4198,7 @@ public class ULocaleTest extends TestFmwk {
 
         val = loc3.getKeywordValue("numbers");
         assertEquals("Default, ICU keyword", null, val);
-        
+
         // Note: ICU does not have getUnicodeKeywordValue()
     }