]> granicus.if.org Git - icu/commitdiff
ICU-20700 reimplement acceptLanguage() using the LocaleMatcher; replace older accept...
authorMarkus Scherer <markus.icu@gmail.com>
Sat, 7 Mar 2020 01:23:17 +0000 (17:23 -0800)
committerMarkus Scherer <markus.icu@gmail.com>
Sun, 8 Mar 2020 15:01:31 +0000 (08:01 -0700)
icu4c/source/common/localematcher.cpp
icu4c/source/common/localeprioritylist.cpp
icu4c/source/common/uloc.cpp
icu4c/source/test/cintltst/cloctst.c
icu4j/main/classes/collate/src/com/ibm/icu/util/GlobalizationPreferences.java
icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java
icu4j/main/tests/collate/src/com/ibm/icu/dev/test/format/GlobalizationPreferencesTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java

index 381df212655859b10fcfbb6e2b80a7220c106338..f5f3a62708875e064c2ead246e9bcd7eafe5597b 100644 (file)
@@ -12,6 +12,7 @@
 #include "unicode/localematcher.h"
 #include "unicode/locid.h"
 #include "unicode/stringpiece.h"
+#include "unicode/uloc.h"
 #include "unicode/uobject.h"
 #include "cstring.h"
 #include "localeprioritylist.h"
@@ -20,6 +21,7 @@
 #include "lsr.h"
 #include "uassert.h"
 #include "uhash.h"
+#include "ustr_imp.h"
 #include "uvector.h"
 
 #define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR)
@@ -688,4 +690,97 @@ double LocaleMatcher::internalMatch(const Locale &desired, const Locale &support
 
 U_NAMESPACE_END
 
+// uloc_acceptLanguage() --------------------------------------------------- ***
+
+U_NAMESPACE_USE
+
+namespace {
+
+class LocaleFromTag {
+public:
+    LocaleFromTag() : locale(Locale::getRoot()) {}
+    const Locale &operator()(const char *tag) { return locale = Locale(tag); }
+
+private:
+    // Store the locale in the converter, rather than return a reference to a temporary,
+    // or a value which could go out of scope with the caller's reference to it.
+    Locale locale;
+};
+
+int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales,
+                       char *dest, int32_t capacity, UAcceptResult *acceptResult,
+                       UErrorCode &errorCode) {
+    if (U_FAILURE(errorCode)) { return 0; }
+    LocaleMatcher::Builder builder;
+    const char *locString;
+    while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) {
+        Locale loc(locString);
+        if (loc.isBogus()) {
+            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
+            return 0;
+        }
+        builder.addSupportedLocale(loc);
+    }
+    LocaleMatcher matcher = builder.build(errorCode);
+    LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode);
+    if (U_FAILURE(errorCode)) { return 0; }
+    if (result.getDesiredIndex() >= 0) {
+        if (acceptResult != nullptr) {
+            *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ?
+                ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK;
+        }
+        const char *bestStr = result.getSupportedLocale()->getName();
+        int32_t bestLength = (int32_t)uprv_strlen(bestStr);
+        if (bestLength <= capacity) {
+            uprv_memcpy(dest, bestStr, bestLength);
+        }
+        return u_terminateChars(dest, capacity, bestLength, &errorCode);
+    } else {
+        if (acceptResult != nullptr) {
+            *acceptResult = ULOC_ACCEPT_FAILED;
+        }
+        return u_terminateChars(dest, capacity, 0, &errorCode);
+    }
+}
+
+}  // namespace
+
+U_CAPI int32_t U_EXPORT2
+uloc_acceptLanguage(char *result, int32_t resultAvailable,
+                    UAcceptResult *outResult,
+                    const char **acceptList, int32_t acceptListCount,
+                    UEnumeration *availableLocales,
+                    UErrorCode *status) {
+    if (U_FAILURE(*status)) { return 0; }
+    if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
+            (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) ||
+            availableLocales == nullptr) {
+        *status = U_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
+    }
+    LocaleFromTag converter;
+    Locale::ConvertingIterator<const char **, LocaleFromTag> desiredLocales(
+        acceptList, acceptList + acceptListCount, converter);
+    return acceptLanguage(*availableLocales, desiredLocales,
+                          result, resultAvailable, outResult, *status);
+}
+
+U_CAPI int32_t U_EXPORT2
+uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable,
+                            UAcceptResult *outResult,
+                            const char *httpAcceptLanguage,
+                            UEnumeration *availableLocales,
+                            UErrorCode *status) {
+    if (U_FAILURE(*status)) { return 0; }
+    if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
+            httpAcceptLanguage == nullptr || availableLocales == nullptr) {
+        *status = U_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
+    }
+    LocalePriorityList list(httpAcceptLanguage, *status);
+    LocalePriorityList::Iterator desiredLocales = list.iterator();
+    return acceptLanguage(*availableLocales, desiredLocales,
+                          result, resultAvailable, outResult, *status);
+}
+
 #endif  // __LOCMATCHER_H__
index 06442fb46a83ad9d250425dc87c993146be9a6db..cee408269c9b3934b97f950b4bc9097ac0788f8e 100644 (file)
@@ -133,7 +133,7 @@ LocalePriorityList::LocalePriorityList(StringPiece s, UErrorCode &errorCode) {
         if (U_FAILURE(errorCode)) { return; }
         Locale locale = Locale(tag.data());
         if (locale.isBogus()) {
-            errorCode = U_MEMORY_ALLOCATION_ERROR;
+            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
             return;
         }
         int32_t weight = WEIGHT_ONE;
index 687390c5b61ff5796a5df9a41440b9f96829b3e5..28cee286e2084120d349d8b3bc6a82029f7c1cb0 100644 (file)
@@ -2127,301 +2127,6 @@ uloc_getISOCountries()
     return COUNTRIES;
 }
 
-
-/* this function to be moved into cstring.c later */
-static char gDecimal = 0;
-
-static /* U_CAPI */
-double
-/* U_EXPORT2 */
-_uloc_strtod(const char *start, char **end) {
-    char *decimal;
-    char *myEnd;
-    char buf[30];
-    double rv;
-    if (!gDecimal) {
-        char rep[5];
-        /* For machines that decide to change the decimal on you,
-        and try to be too smart with localization.
-        This normally should be just a '.'. */
-        sprintf(rep, "%+1.1f", 1.0);
-        gDecimal = rep[2];
-    }
-
-    if(gDecimal == '.') {
-        return uprv_strtod(start, end); /* fall through to OS */
-    } else {
-        uprv_strncpy(buf, start, 29);
-        buf[29]=0;
-        decimal = uprv_strchr(buf, '.');
-        if(decimal) {
-            *decimal = gDecimal;
-        } else {
-            return uprv_strtod(start, end); /* no decimal point */
-        }
-        rv = uprv_strtod(buf, &myEnd);
-        if(end) {
-            *end = (char*)(start+(myEnd-buf)); /* cast away const (to follow uprv_strtod API.) */
-        }
-        return rv;
-    }
-}
-
-typedef struct {
-    float q;
-    int32_t dummy;  /* to avoid uninitialized memory copy from qsort */
-    char locale[ULOC_FULLNAME_CAPACITY+1];
-} _acceptLangItem;
-
-static int32_t U_CALLCONV
-uloc_acceptLanguageCompare(const void * /*context*/, const void *a, const void *b)
-{
-    const _acceptLangItem *aa = (const _acceptLangItem*)a;
-    const _acceptLangItem *bb = (const _acceptLangItem*)b;
-
-    int32_t rc = 0;
-    if(bb->q < aa->q) {
-        rc = -1;  /* A > B */
-    } else if(bb->q > aa->q) {
-        rc = 1;   /* A < B */
-    } else {
-        rc = 0;   /* A = B */
-    }
-
-    if(rc==0) {
-        rc = uprv_stricmp(aa->locale, bb->locale);
-    }
-
-#if defined(ULOC_DEBUG)
-    /*  fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n",
-    aa->locale, aa->q,
-    bb->locale, bb->q,
-    rc);*/
-#endif
-
-    return rc;
-}
-
-/*
-mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53
-*/
-
-U_CAPI int32_t U_EXPORT2
-uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, UAcceptResult *outResult,
-                            const char *httpAcceptLanguage,
-                            UEnumeration* availableLocales,
-                            UErrorCode *status)
-{
-  MaybeStackArray<_acceptLangItem, 4> items; // Struct for collecting items.
-    char tmp[ULOC_FULLNAME_CAPACITY +1];
-    int32_t n = 0;
-    const char *itemEnd;
-    const char *paramEnd;
-    const char *s;
-    const char *t;
-    int32_t res;
-    int32_t i;
-    int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage);
-
-    if(U_FAILURE(*status)) {
-        return -1;
-    }
-
-    for(s=httpAcceptLanguage;s&&*s;) {
-        while(isspace(*s)) /* eat space at the beginning */
-            s++;
-        itemEnd=uprv_strchr(s,',');
-        paramEnd=uprv_strchr(s,';');
-        if(!itemEnd) {
-            itemEnd = httpAcceptLanguage+l; /* end of string */
-        }
-        if(paramEnd && paramEnd<itemEnd) {
-            /* semicolon (;) is closer than end (,) */
-            t = paramEnd+1;
-            if(*t=='q') {
-                t++;
-            }
-            while(isspace(*t)) {
-                t++;
-            }
-            if(*t=='=') {
-                t++;
-            }
-            while(isspace(*t)) {
-                t++;
-            }
-            items[n].q = (float)_uloc_strtod(t,NULL);
-        } else {
-            /* no semicolon - it's 1.0 */
-            items[n].q = 1.0f;
-            paramEnd = itemEnd;
-        }
-        items[n].dummy=0;
-        /* eat spaces prior to semi */
-        for(t=(paramEnd-1);(paramEnd>s)&&isspace(*t);t--)
-            ;
-        int32_t slen = static_cast<int32_t>(((t+1)-s));
-        if(slen > ULOC_FULLNAME_CAPACITY) {
-          *status = U_BUFFER_OVERFLOW_ERROR;
-          return -1; // too big
-        }
-        uprv_strncpy(items[n].locale, s, slen);
-        items[n].locale[slen]=0; // terminate
-        int32_t clen = uloc_canonicalize(items[n].locale, tmp, UPRV_LENGTHOF(tmp)-1, status);
-        if(U_FAILURE(*status)) return -1;
-        if((clen!=slen) || (uprv_strncmp(items[n].locale, tmp, slen))) {
-            // canonicalization had an effect- copy back
-            uprv_strncpy(items[n].locale, tmp, clen);
-            items[n].locale[clen] = 0; // terminate
-        }
-#if defined(ULOC_DEBUG)
-        /*fprintf(stderr,"%d: s <%s> q <%g>\n", n, j[n].locale, j[n].q);*/
-#endif
-        n++;
-        s = itemEnd;
-        while(*s==',') { /* eat duplicate commas */
-            s++;
-        }
-        if(n>=items.getCapacity()) { // If we need more items
-          if(NULL == items.resize(items.getCapacity()*2, items.getCapacity())) {
-              *status = U_MEMORY_ALLOCATION_ERROR;
-              return -1;
-          }
-#if defined(ULOC_DEBUG)
-          fprintf(stderr,"malloced at size %d\n", items.getCapacity());
-#endif
-        }
-    }
-    uprv_sortArray(items.getAlias(), n, sizeof(items[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
-    if (U_FAILURE(*status)) {
-        return -1;
-    }
-    LocalMemory<const char*> strs(NULL);
-    if (strs.allocateInsteadAndReset(n) == NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return -1;
-    }
-    for(i=0;i<n;i++) {
-#if defined(ULOC_DEBUG)
-        /*fprintf(stderr,"%d: s <%s> q <%g>\n", i, j[i].locale, j[i].q);*/
-#endif
-        strs[i]=items[i].locale;
-    }
-    res =  uloc_acceptLanguage(result, resultAvailable, outResult,
-                               strs.getAlias(), n, availableLocales, status);
-    return res;
-}
-
-
-U_CAPI int32_t U_EXPORT2
-uloc_acceptLanguage(char *result, int32_t resultAvailable,
-                    UAcceptResult *outResult, const char **acceptList,
-                    int32_t acceptListCount,
-                    UEnumeration* availableLocales,
-                    UErrorCode *status)
-{
-    int32_t i,j;
-    int32_t len;
-    int32_t maxLen=0;
-    char tmp[ULOC_FULLNAME_CAPACITY+1];
-    const char *l;
-    char **fallbackList;
-    if(U_FAILURE(*status)) {
-        return -1;
-    }
-    fallbackList = static_cast<char **>(uprv_malloc((size_t)(sizeof(fallbackList[0])*acceptListCount)));
-    if(fallbackList==NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return -1;
-    }
-    for(i=0;i<acceptListCount;i++) {
-#if defined(ULOC_DEBUG)
-        fprintf(stderr,"%02d: %s\n", i, acceptList[i]);
-#endif
-        while((l=uenum_next(availableLocales, NULL, status)) != NULL) {
-#if defined(ULOC_DEBUG)
-            fprintf(stderr,"  %s\n", l);
-#endif
-            len = (int32_t)uprv_strlen(l);
-            if(!uprv_strcmp(acceptList[i], l)) {
-                if(outResult) {
-                    *outResult = ULOC_ACCEPT_VALID;
-                }
-#if defined(ULOC_DEBUG)
-                fprintf(stderr, "MATCH! %s\n", l);
-#endif
-                if(len>0) {
-                    uprv_strncpy(result, l, uprv_min(len, resultAvailable));
-                }
-                for(j=0;j<i;j++) {
-                    uprv_free(fallbackList[j]);
-                }
-                uprv_free(fallbackList);
-                return u_terminateChars(result, resultAvailable, len, status);
-            }
-            if(len>maxLen) {
-                maxLen = len;
-            }
-        }
-        uenum_reset(availableLocales, status);
-        /* save off parent info */
-        if(uloc_getParent(acceptList[i], tmp, UPRV_LENGTHOF(tmp), status)!=0) {
-            fallbackList[i] = uprv_strdup(tmp);
-        } else {
-            fallbackList[i]=0;
-        }
-    }
-
-    for(maxLen--;maxLen>0;maxLen--) {
-        for(i=0;i<acceptListCount;i++) {
-            if(fallbackList[i] && ((int32_t)uprv_strlen(fallbackList[i])==maxLen)) {
-#if defined(ULOC_DEBUG)
-                fprintf(stderr,"Try: [%s]", fallbackList[i]);
-#endif
-                while((l=uenum_next(availableLocales, NULL, status)) != NULL) {
-#if defined(ULOC_DEBUG)
-                    fprintf(stderr,"  %s\n", l);
-#endif
-                    len = (int32_t)uprv_strlen(l);
-                    if(!uprv_strcmp(fallbackList[i], l)) {
-                        if(outResult) {
-                            *outResult = ULOC_ACCEPT_FALLBACK;
-                        }
-#if defined(ULOC_DEBUG)
-                        fprintf(stderr, "fallback MATCH! %s\n", l);
-#endif
-                        if(len>0) {
-                            uprv_strncpy(result, l, uprv_min(len, resultAvailable));
-                        }
-                        for(j=0;j<acceptListCount;j++) {
-                            uprv_free(fallbackList[j]);
-                        }
-                        uprv_free(fallbackList);
-                        return u_terminateChars(result, resultAvailable, len, status);
-                    }
-                }
-                uenum_reset(availableLocales, status);
-
-                if(uloc_getParent(fallbackList[i], tmp, UPRV_LENGTHOF(tmp), status)!=0) {
-                    uprv_free(fallbackList[i]);
-                    fallbackList[i] = uprv_strdup(tmp);
-                } else {
-                    uprv_free(fallbackList[i]);
-                    fallbackList[i]=0;
-                }
-            }
-        }
-        if(outResult) {
-            *outResult = ULOC_ACCEPT_FAILED;
-        }
-    }
-    for(i=0;i<acceptListCount;i++) {
-        uprv_free(fallbackList[i]);
-    }
-    uprv_free(fallbackList);
-    return -1;
-}
-
 U_CAPI const char* U_EXPORT2
 uloc_toUnicodeLocaleKey(const char* keyword)
 {
index affdecd13e4cc42a58f83aecfbd48cc877a70cf7..22a05055c6360acfdd98bcacfce52ee558cfbe37 100644 (file)
@@ -2988,30 +2988,33 @@ static void TestAcceptLanguage(void) {
     } tests[] = { 
         /*0*/{ 0, NULL, "mt_MT", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
         /*1*/{ 1, NULL, "en", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
-        /*2*/{ 2, NULL, "en", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},
+        /*2*/{ 2, NULL, "en_GB", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},
         /*3*/{ 3, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR},
         /*4*/{ 4, NULL, "es", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
-        /*5*/{ 5, NULL, "en", ULOC_ACCEPT_VALID, U_ZERO_ERROR},  /* XF */
+        /*5*/{ 5, NULL, "zh", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},  /* XF */
         /*6*/{ 6, NULL, "ja", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},  /* XF */
         /*7*/{ 7, NULL, "zh", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},  /* XF */
-        /*8*/{ 8, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR },  /*  */
-        /*9*/{ 9, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR },  /*  */
-       /*10*/{10, NULL, "", ULOC_ACCEPT_FAILED, U_BUFFER_OVERFLOW_ERROR },  /*  */
-       /*11*/{11, NULL, "", ULOC_ACCEPT_FAILED, U_BUFFER_OVERFLOW_ERROR },  /*  */
+        /*8*/{ 8, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR },  /*  */
+        /*9*/{ 9, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR },  /*  */
+       /*10*/{10, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR },  /*  */
+       /*11*/{11, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR },  /*  */
     };
     const int32_t numTests = UPRV_LENGTHOF(tests);
     static const char *http[] = {
-        /*0*/ "mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53, th-th-traditional;q=.01",
+        /*0*/ "mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, "
+              "iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, "
+              "es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, "
+              "nl-nl;q=0.55, nl;q=0.53, th-th-traditional;q=0.01",
         /*1*/ "ja;q=0.5, en;q=0.8, tlh",
         /*2*/ "en-wf, de-lx;q=0.8",
-        /*3*/ "mga-ie;q=0.9, tlh",
-        /*4*/ "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "
-              "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xx-yy;q=.1, "
+        /*3*/ "mga-ie;q=0.9, sux",
+        /*4*/ "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, "
+              "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xx-yy;q=0.1, "
               "es",
         /*5*/ "zh-xx;q=0.9, en;q=0.6",
         /*6*/ "ja-JA",
@@ -3042,12 +3045,17 @@ static void TestAcceptLanguage(void) {
 
         available = ures_openAvailableLocales(tests[i].icuSet, &status);
         tmp[0]=0;
-        rc = uloc_acceptLanguageFromHTTP(tmp, 199, &outResult, http[tests[i].httpSet], available, &status);
+        rc = uloc_acceptLanguageFromHTTP(tmp, 199, &outResult,
+                                         http[tests[i].httpSet], available, &status);
         (void)rc;    /* Suppress set but not used warning. */
         uenum_close(available);
-        log_verbose(" got %s, %s [%s]\n", tmp[0]?tmp:"(EMPTY)", acceptResult(outResult), u_errorName(status));
+        log_verbose(" got %s, %s [%s]\n",
+                    tmp[0]?tmp:"(EMPTY)", acceptResult(outResult), u_errorName(status));
         if(status != tests[i].expectStatus) {
-          log_err_status(status, "FAIL: expected status %s but got %s\n", u_errorName(tests[i].expectStatus), u_errorName(status));
+          log_err_status(status,
+                         "FAIL: expected status %s but got %s\n",
+                         u_errorName(tests[i].expectStatus),
+                         u_errorName(status));
         } else if(U_SUCCESS(tests[i].expectStatus)) {
             /* don't check content if expected failure */
             if(outResult != tests[i].res) {
@@ -3055,10 +3063,13 @@ static void TestAcceptLanguage(void) {
                 acceptResult( tests[i].res), 
                 acceptResult( outResult));
             log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n", 
-                i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect,acceptResult(tests[i].res));
+                     i, http[tests[i].httpSet], tests[i].icuSet,
+                     tests[i].expect,acceptResult(tests[i].res));
             }
             if((outResult>0)&&uprv_strcmp(tmp, tests[i].expect)) {
-              log_err_status(status, "FAIL: #%d: expected %s but got %s\n", i, tests[i].expect, tmp);
+              log_err_status(status,
+                             "FAIL: #%d: expected %s but got %s\n",
+                             i, tests[i].expect, tmp);
               log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n", 
                        i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect, acceptResult(tests[i].res));
             }
index 13c9e081bdfc02cb273be64be12686a7c3d26b93..5c3699c0e4cff6b1ce7f43e37c9ad88ba350f9a3 100644 (file)
@@ -8,7 +8,6 @@
 */
 package com.ibm.icu.util;
 
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -18,6 +17,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
+import java.util.Set;
 
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.BreakIterator;
@@ -259,14 +259,10 @@ public class GlobalizationPreferences implements Freezable<GlobalizationPreferen
         if (isFrozen()) {
             throw new UnsupportedOperationException("Attempt to modify immutable object");
         }
-        ULocale[] acceptLocales = null;
-        try {
-            acceptLocales = ULocale.parseAcceptLanguage(acceptLanguageString, true);
-        } catch (ParseException pe) {
-            //TODO: revisit after 3.8
-            throw new IllegalArgumentException("Invalid Accept-Language string");
-        }
-        return setLocales(acceptLocales);
+        Set<ULocale> acceptSet = LocalePriorityList.add(acceptLanguageString).build().getULocales();
+        // processLocales() wants a List even though it only iterates front-to-back.
+        locales = processLocales(new ArrayList<>(acceptSet));
+        return this;
     }
 
     /**
@@ -815,6 +811,9 @@ public class GlobalizationPreferences implements Freezable<GlobalizationPreferen
      * @provisional This API might change or be removed in a future release.
      */
     protected List<ULocale> processLocales(List<ULocale> inputLocales) {
+        // Note: Some of the callers, and non-ICU call sites, could be simpler/more efficient
+        // if this method took a Collection or even an Iterable.
+        // Maybe we can change it since this is still @draft and probably not widely overridden.
         List<ULocale> result = new ArrayList<>();
         /*
          * Step 1: Relocate later occurrence of more specific locale
@@ -824,9 +823,7 @@ public class GlobalizationPreferences implements Freezable<GlobalizationPreferen
          *   Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA
          *   After  - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
          */
-        for (int i = 0; i < inputLocales.size(); i++) {
-            ULocale uloc = inputLocales.get(i);
-
+        for (ULocale uloc : inputLocales) {
             String language = uloc.getLanguage();
             String script = uloc.getScript();
             String country = uloc.getCountry();
index 00659a381cf8b270bf56bca6c89966dceb95765c..ac8746f70615f950b00f4f0844d10e6fb9bd599d 100644 (file)
@@ -12,7 +12,6 @@ package com.ibm.icu.util;
 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;
@@ -34,7 +33,6 @@ import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.impl.ICUResourceTableAccess;
 import com.ibm.icu.impl.LocaleIDParser;
 import com.ibm.icu.impl.LocaleIDs;
-import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.impl.SoftCache;
 import com.ibm.icu.impl.locale.AsciiUtil;
 import com.ibm.icu.impl.locale.BaseLocale;
@@ -417,15 +415,6 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
         this.locale = locale;
     }
 
-    /**
-     * Construct a ULocale object from a {@link java.util.Locale}.
-     * @param loc a {@link java.util.Locale}
-     */
-    private ULocale(Locale loc) {
-        this.localeID = getName(forLocale(loc).toString());
-        this.locale = loc;
-    }
-
     /**
      * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
      * The ULocale is canonicalized.
@@ -2130,28 +2119,42 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
      * availableLocales matched).  No ULocale array element should be null; behavior is
      * undefined if this is the case.
+     *
+     * <p>This is a thin wrapper over {@link LocalePriorityList} + {@link LocaleMatcher}.
+     *
      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
      * @param availableLocales list of available locales. One of these will be returned.
      * @param fallback if non-null, a 1-element array containing a boolean to be set with
      * the fallback status
      * @return one of the locales from the availableLocales list, or null if none match
      * @stable ICU 3.4
+     * @see LocaleMatcher
+     * @see LocalePriorityList
      */
     public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
             boolean[] fallback) {
-        if (acceptLanguageList == null) {
-            throw new NullPointerException();
+        if (fallback != null) {
+            fallback[0] = true;
         }
-        ULocale acceptList[] = null;
+        LocalePriorityList desired;
         try {
-            acceptList = parseAcceptLanguage(acceptLanguageList, true);
-        } catch (ParseException pe) {
-            acceptList = null;
-        }
-        if (acceptList == null) {
+            desired = LocalePriorityList.add(acceptLanguageList).build();
+        } catch (IllegalArgumentException e) {
             return null;
         }
-        return acceptLanguage(acceptList, availableLocales, fallback);
+        LocaleMatcher.Builder builder = LocaleMatcher.builder();
+        for (ULocale locale : availableLocales) {
+            builder.addSupportedULocale(locale);
+        }
+        LocaleMatcher matcher = builder.build();
+        LocaleMatcher.Result result = matcher.getBestMatchResult(desired);
+        if (result.getDesiredIndex() >= 0) {
+            if (fallback != null && result.getDesiredULocale().equals(result.getSupportedULocale())) {
+                fallback[0] = false;
+            }
+            return result.getSupportedULocale();
+        }
+        return null;
     }
 
     /**
@@ -2162,57 +2165,39 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
      * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
      * locale was used as a fallback (because nothing else in availableLocales matched).
      * No ULocale array element should be null; behavior is undefined if this is the case.
+     *
+     * <p>This is a thin wrapper over {@link LocaleMatcher}.
+     *
      * @param acceptLanguageList list of acceptable locales
      * @param availableLocales list of available locales. One of these will be returned.
      * @param fallback if non-null, a 1-element array containing a boolean to be set with
      * the fallback status
      * @return one of the locales from the availableLocales list, or null if none match
      * @stable ICU 3.4
+     * @see LocaleMatcher
      */
 
-    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
-            availableLocales, boolean[] fallback) {
-        // fallbacklist
-        int i,j;
-        if(fallback != null) {
-            fallback[0]=true;
+    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[] availableLocales,
+            boolean[] fallback) {
+        if (fallback != null) {
+            fallback[0] = true;
         }
-        for(i=0;i<acceptLanguageList.length;i++) {
-            ULocale aLocale = acceptLanguageList[i];
-            boolean[] setFallback = fallback;
-            do {
-                for(j=0;j<availableLocales.length;j++) {
-                    if(availableLocales[j].equals(aLocale)) {
-                        if(setFallback != null) {
-                            setFallback[0]=false; // first time with this locale - not a fallback.
-                        }
-                        return availableLocales[j];
-                    }
-                    // compare to scriptless alias, so locales such as
-                    // zh_TW, zh_CN are considered as available locales - see #7190
-                    if (aLocale.getScript().length() == 0
-                            && availableLocales[j].getScript().length() > 0
-                            && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
-                            && availableLocales[j].getCountry().equals(aLocale.getCountry())
-                            && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
-                        ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
-                        if (minAvail.getScript().length() == 0) {
-                            if(setFallback != null) {
-                                setFallback[0] = false; // not a fallback.
-                            }
-                            return aLocale;
-                        }
-                    }
-                }
-                Locale loc = aLocale.toLocale();
-                Locale parent = LocaleUtility.fallback(loc);
-                if(parent != null) {
-                    aLocale = new ULocale(parent);
-                } else {
-                    aLocale = null;
-                }
-                setFallback = null; // Do not set fallback in later iterations
-            } while (aLocale != null);
+        LocaleMatcher.Builder builder = LocaleMatcher.builder();
+        for (ULocale locale : availableLocales) {
+            builder.addSupportedULocale(locale);
+        }
+        LocaleMatcher matcher = builder.build();
+        LocaleMatcher.Result result;
+        if (acceptLanguageList.length == 1) {
+            result = matcher.getBestMatchResult(acceptLanguageList[0]);
+        } else {
+            result = matcher.getBestMatchResult(Arrays.asList(acceptLanguageList));
+        }
+        if (result.getDesiredIndex() >= 0) {
+            if (fallback != null && result.getDesiredULocale().equals(result.getSupportedULocale())) {
+                fallback[0] = false;
+            }
+            return result.getSupportedULocale();
         }
         return null;
     }
@@ -2227,12 +2212,17 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
      * availableLocales matched).  No ULocale array element should be null; behavior is
      * undefined if this is the case.  This function will choose a locale from the
      * ULocale.getAvailableLocales() list as available.
+     *
+     * <p>This is a thin wrapper over {@link LocalePriorityList} + {@link LocaleMatcher}.
+     *
      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
      * @param fallback if non-null, a 1-element array containing a boolean to be set with
      * the fallback status
      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
      * none match
      * @stable ICU 3.4
+     * @see LocaleMatcher
+     * @see LocalePriorityList
      */
     public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
@@ -2249,275 +2239,21 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
      * availableLocales matched).  No ULocale array element should be null; behavior is
      * undefined if this is the case.  This function will choose a locale from the
      * ULocale.getAvailableLocales() list as available.
+     *
+     * <p>This is a thin wrapper over {@link LocaleMatcher}.
+     *
      * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
      * @param fallback if non-null, a 1-element array containing a boolean to be set with
      * the fallback status
      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
      * @stable ICU 3.4
+     * @see LocaleMatcher
      */
     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
                 fallback);
     }
 
-    /**
-     * Package local method used for parsing Accept-Language string
-     */
-    static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
-            throws ParseException {
-        class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
-            private double q;
-            private double serial;
-            public ULocaleAcceptLanguageQ(double theq, int theserial) {
-                q = theq;
-                serial = theserial;
-            }
-            @Override
-            public int compareTo(ULocaleAcceptLanguageQ other) {
-                if (q > other.q) { // reverse - to sort in descending order
-                    return -1;
-                } else if (q < other.q) {
-                    return 1;
-                }
-                if (serial < other.serial) {
-                    return -1;
-                } else if (serial > other.serial) {
-                    return 1;
-                } else {
-                    return 0; // same object
-                }
-            }
-        }
-
-        // parse out the acceptLanguage into an array
-        TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
-                new TreeMap<>();
-        StringBuilder languageRangeBuf = new StringBuilder();
-        StringBuilder qvalBuf = new StringBuilder();
-        int state = 0;
-        acceptLanguage += ","; // append comma to simplify the parsing code
-        int n;
-        boolean subTag = false;
-        boolean q1 = false;
-        for (n = 0; n < acceptLanguage.length(); n++) {
-            boolean gotLanguageQ = false;
-            char c = acceptLanguage.charAt(n);
-            switch (state) {
-            case 0: // before language-range start
-                if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
-                    // in language-range
-                    languageRangeBuf.append(c);
-                    state = 1;
-                    subTag = false;
-                } else if (c == '*') {
-                    languageRangeBuf.append(c);
-                    state = 2;
-                } else if (c != ' ' && c != '\t') {
-                    // invalid character
-                    state = -1;
-                }
-                break;
-            case 1: // in language-range
-                if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
-                    languageRangeBuf.append(c);
-                } else if (c == '-') {
-                    subTag = true;
-                    languageRangeBuf.append(c);
-                } else if (c == '_') {
-                    if (isLenient) {
-                        subTag = true;
-                        languageRangeBuf.append(c);
-                    } else {
-                        state = -1;
-                    }
-                } else if ('0' <= c && c <= '9') {
-                    if (subTag) {
-                        languageRangeBuf.append(c);
-                    } else {
-                        // DIGIT is allowed only in language sub tag
-                        state = -1;
-                    }
-                } else if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c == ' ' || c == '\t') {
-                    // language-range end
-                    state = 3;
-                } else if (c == ';') {
-                    // before q
-                    state = 4;
-                } else {
-                    // invalid character for language-range
-                    state = -1;
-                }
-                break;
-            case 2: // saw wild card range
-                if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c == ' ' || c == '\t') {
-                    // language-range end
-                    state = 3;
-                } else if (c == ';') {
-                    // before q
-                    state = 4;
-                } else {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 3: // language-range end
-                if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c == ';') {
-                    // before q
-                    state =4;
-                } else if (c != ' ' && c != '\t') {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 4: // before q
-                if (c == 'q') {
-                    // before equal
-                    state = 5;
-                } else if (c != ' ' && c != '\t') {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 5: // before equal
-                if (c == '=') {
-                    // before q value
-                    state = 6;
-                } else if (c != ' ' && c != '\t') {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 6: // before q value
-                if (c == '0') {
-                    // q value start with 0
-                    q1 = false;
-                    qvalBuf.append(c);
-                    state = 7;
-                } else if (c == '1') {
-                    // q value start with 1
-                    qvalBuf.append(c);
-                    state = 7;
-                } else if (c == '.') {
-                    if (isLenient) {
-                        qvalBuf.append(c);
-                        state = 8;
-                    } else {
-                        state = -1;
-                    }
-                } else if (c != ' ' && c != '\t') {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 7: // q value start
-                if (c == '.') {
-                    // before q value fraction part
-                    qvalBuf.append(c);
-                    state = 8;
-                } else if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c == ' ' || c == '\t') {
-                    // after q value
-                    state = 10;
-                } else {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 8: // before q value fraction part
-                if ('0' <= c && c <= '9') {
-                    if (q1 && c != '0' && !isLenient) {
-                        // if q value starts with 1, the fraction part must be 0
-                        state = -1;
-                    } else {
-                        // in q value fraction part
-                        qvalBuf.append(c);
-                        state = 9;
-                    }
-                } else {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 9: // in q value fraction part
-                if ('0' <= c && c <= '9') {
-                    if (q1 && c != '0') {
-                        // if q value starts with 1, the fraction part must be 0
-                        state = -1;
-                    } else {
-                        qvalBuf.append(c);
-                    }
-                } else if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c == ' ' || c == '\t') {
-                    // after q value
-                    state = 10;
-                } else {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            case 10: // after q value
-                if (c == ',') {
-                    // language-q end
-                    gotLanguageQ = true;
-                } else if (c != ' ' && c != '\t') {
-                    // invalid
-                    state = -1;
-                }
-                break;
-            }
-            if (state == -1) {
-                // error state
-                throw new ParseException("Invalid Accept-Language", n);
-            }
-            if (gotLanguageQ) {
-                double q = 1.0;
-                if (qvalBuf.length() != 0) {
-                    try {
-                        q = Double.parseDouble(qvalBuf.toString());
-                    } catch (NumberFormatException nfe) {
-                        // Already validated, so it should never happen
-                        q = 1.0;
-                    }
-                    if (q > 1.0) {
-                        q = 1.0;
-                    }
-                }
-                if (languageRangeBuf.charAt(0) != '*') {
-                    int serial = map.size();
-                    ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
-                    // sort in reverse order..   1.0, 0.9, 0.8 .. etc
-                    map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
-                }
-
-                // reset buffer and parse state
-                languageRangeBuf.setLength(0);
-                qvalBuf.setLength(0);
-                state = 0;
-            }
-        }
-        if (state != 0) {
-            // Well, the parser should handle all cases.  So just in case.
-            throw new ParseException("Invalid AcceptlLanguage", n);
-        }
-
-        // pull out the map
-        ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
-        return acceptList;
-    }
-
     private static final String UNDEFINED_LANGUAGE = "und";
     private static final String UNDEFINED_SCRIPT = "Zzzz";
     private static final String UNDEFINED_REGION = "ZZ";
@@ -3396,7 +3132,7 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
 
         List<String>subtags = tag.getVariants();
         // ICU-20478: Sort variants per UTS35.
-        ArrayList<String> variants = new ArrayList<String>(subtags);
+        ArrayList<String> variants = new ArrayList<>(subtags);
         Collections.sort(variants);
         for (String s : variants) {
             buf.append(LanguageTag.SEP);
index 627d286f7e5a8866ba98c2a5b832f0d4908ed66f..cd5c746440914b2068b77ab7a1c7ccbb7e2fb84d 100644 (file)
@@ -239,7 +239,7 @@ public class GlobalizationPreferencesTest extends TestFmwk {
         {"fr_CA", "fr"},
         {"fr", "fr_CA"},
         {"es", "fr", "en_US"},
-        {"zh_Hans", "zh_Hans_CN"},
+        {"zh_CN", "zh_Hans", "zh_Hans_CN"},
         {"en_US_123"},
         {"es_US", "es"},
         {"de_DE", "es", "fr_FR"},
@@ -261,7 +261,7 @@ public class GlobalizationPreferencesTest extends TestFmwk {
         {"fr_CA", "fr"},
         {"fr_CA", "fr"},
         {"es", "fr", "en_US", "en"},
-        {"zh_Hans_CN", "zh_Hans", "zh"},
+        {"zh_Hans_CN", "zh_CN", "zh_Hans", "zh"},
         {"en_US_123", "en_US", "en"},
         {"es_US", "es"},
         {"de_DE", "de", "es", "fr_FR", "fr"},
@@ -348,6 +348,11 @@ public class GlobalizationPreferencesTest extends TestFmwk {
             gp.setLocales(acceptLanguage);
 
             List<ULocale> resultLocales = gp.getLocales();
+            List<ULocale> expectedLocales = new ArrayList<>(RESULTS_LOCALEIDS[i].length);
+            for (String exp : RESULTS_LOCALEIDS[i]) {
+                expectedLocales.add(new ULocale(exp));
+            }
+            assertEquals("#" + i, expectedLocales.toString(), resultLocales.toString());
             if (resultLocales.size() != RESULTS_LOCALEIDS[i].length) {
                 StringBuilder res = new StringBuilder();
                 for (ULocale l : resultLocales) {
@@ -377,22 +382,23 @@ public class GlobalizationPreferencesTest extends TestFmwk {
         }
 
         // Invalid accept-language
-        logln("Set locale - ko_KR");
-        gp.setLocale(new ULocale("ko_KR"));
-        boolean bException = false;
-        try {
-            logln("Set invlaid accept-language - ko=100");
-            gp.setLocales("ko=100");
-        } catch (IllegalArgumentException iae) {
-            logln("IllegalArgumentException was thrown");
-            bException = true;
-        }
-        if (!bException) {
-            errln("FAIL: IllegalArgumentException was not thrown for illegal accept-language - ko=100");
-        }
-        if (!gp.getLocale(0).toString().equals("ko_KR")) {
-            errln("FAIL: Previous valid locale list had gone");
-        }
+        // ICU-20700 changed the parser to using LocalePriorityList which is more lenient.
+//        logln("Set locale - ko_KR");
+//        gp.setLocale(new ULocale("ko_KR"));
+//        boolean bException = false;
+//        try {
+//            logln("Set invlaid accept-language - ko=100");
+//            gp.setLocales("ko=100");
+//        } catch (IllegalArgumentException iae) {
+//            logln("IllegalArgumentException was thrown");
+//            bException = true;
+//        }
+//        if (!bException) {
+//            errln("FAIL: IllegalArgumentException was not thrown for illegal accept-language - ko=100");
+//        }
+//        if (!gp.getLocale(0).toString().equals("ko_KR")) {
+//            errln("FAIL: Previous valid locale list had gone");
+//        }
     }
 
     @Test
index 0d00729578d0b7fba28aa1a2c9eed2b42313c846..4b7cc162cb33b25ee71da03688ef46f1fd0e77f5 100644 (file)
@@ -22,7 +22,6 @@ import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.regex.Pattern;
 
@@ -34,7 +33,6 @@ import org.junit.runners.JUnit4;
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.dev.test.TestUtil;
 import com.ibm.icu.dev.test.TestUtil.JavaVendor;
-import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DecimalFormat;
 import com.ibm.icu.text.DisplayContext;
@@ -46,6 +44,7 @@ import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.IllformedLocaleException;
 import com.ibm.icu.util.LocaleData;
+import com.ibm.icu.util.LocalePriorityList;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.ULocale.Builder;
 import com.ibm.icu.util.ULocale.Category;
@@ -1590,15 +1589,15 @@ public class ULocaleTest extends TestFmwk {
         /*3*/ { null, "true" },
         /*4*/ { "es", "false" },
         /*5*/ { "de", "false" },
-        /*6*/ { "zh_Hant_TW", "false" },
-        /*7*/ { "zh", "true" },
+        /*6*/ { "zh_Hant_TW", "true" },
+        /*7*/ { "zh_Hant", "true" },
     };
 
     private static final String ACCEPT_LANGUAGE_HTTP[] = {
         /*0*/ "mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53, th-th-traditional;q=.01",
         /*1*/ "ja;q=0.5, en;q=0.8, tlh",
         /*2*/ "en-zzz, de-lx;q=0.8",
-        /*3*/ "mga-ie;q=0.9, tlh",
+        /*3*/ "mga-ie;q=0.9, sux",
         /*4*/ "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "+
                 "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "+
                 "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "+
@@ -1610,16 +1609,16 @@ public class ULocaleTest extends TestFmwk {
                 "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "+
                 "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, "+
                 "es",
-                /*5*/ "de;q=.9, fr;q=.9, xxx-yyy, sr;q=.8",
-                /*6*/ "zh-tw",
-                /*7*/ "zh-hant-cn",
+        /*5*/ "de;q=.9, fr;q=.9, xxx-yyy, sr;q=.8",
+        /*6*/ "zh-tw",
+        /*7*/ "zh-hant-cn",
     };
 
 
     @Test
     public void TestAcceptLanguage() {
         for(int i = 0 ; i < (ACCEPT_LANGUAGE_HTTP.length); i++) {
-            Boolean expectBoolean = new Boolean(ACCEPT_LANGUAGE_TESTS[i][1]);
+            Boolean expectBoolean = Boolean.valueOf(ACCEPT_LANGUAGE_TESTS[i][1]);
             String expectLocale=ACCEPT_LANGUAGE_TESTS[i][0];
 
             logln("#" + i + ": expecting: " + expectLocale + " (" + expectBoolean + ")");
@@ -1627,128 +1626,50 @@ public class ULocaleTest extends TestFmwk {
             boolean r[] = { false };
             ULocale n = ULocale.acceptLanguage(ACCEPT_LANGUAGE_HTTP[i], r);
             if((n==null)&&(expectLocale!=null)) {
-                errln("result was null! line #" + i);
+                errln("#" + i + ": result was null!");
                 continue;
             }
             if(((n==null)&&(expectLocale==null)) || (n.toString().equals(expectLocale))) {
-                logln(" locale: OK." );
+                logln("#" + i + ": locale: OK." );
             } else {
-                errln("expected " + expectLocale + " but got " + n.toString());
+                errln("#" + i + ": locale: expected " + expectLocale + " but got " + n);
             }
-            if(expectBoolean.equals(new Boolean(r[0]))) {
-                logln(" bool: OK.");
+            Boolean actualBoolean = Boolean.valueOf(r[0]);
+            if(expectBoolean.equals(actualBoolean)) {
+                logln("#" + i + ": fallback: OK.");
             } else {
-                errln("bool: not OK, was " + new Boolean(r[0]).toString() + " expected " + expectBoolean.toString());
+                errln("#" + i + ": fallback: was " + actualBoolean + " expected " + expectBoolean);
             }
         }
     }
 
-    private ULocale[] StringToULocaleArray(String acceptLanguageList){
-        //following code is copied from
-        //ULocale.acceptLanguage(String acceptLanguageList, ULocale[] availableLocales, boolean[] fallback)
-        class ULocaleAcceptLanguageQ implements Comparable {
-            private double q;
-            private double serial;
-            public ULocaleAcceptLanguageQ(double theq, int theserial) {
-                q = theq;
-                serial = theserial;
-            }
-            @Override
-            public int compareTo(Object o) {
-                ULocaleAcceptLanguageQ other = (ULocaleAcceptLanguageQ) o;
-                if(q > other.q) { // reverse - to sort in descending order
-                    return -1;
-                } else if(q < other.q) {
-                    return 1;
-                }
-                if(serial < other.serial) {
-                    return -1;
-                } else if(serial > other.serial) {
-                    return 1;
-                } else {
-                    return 0; // same object
-                }
-            }
-        }
-
-        // 1st: parse out the acceptLanguageList into an array
-
-        TreeMap map = new TreeMap();
-
-        final int l = acceptLanguageList.length();
-        int n;
-        for(n=0;n<l;n++) {
-            int itemEnd = acceptLanguageList.indexOf(',',n);
-            if(itemEnd == -1) {
-                itemEnd = l;
-            }
-            int paramEnd = acceptLanguageList.indexOf(';',n);
-            double q = 1.0;
-
-            if((paramEnd != -1) && (paramEnd < itemEnd)) {
-                /* semicolon (;) is closer than end (,) */
-                int t = paramEnd + 1;
-                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
-                    t++;
-                }
-                if(acceptLanguageList.charAt(t)=='q') {
-                    t++;
-                }
-                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
-                    t++;
-                }
-                if(acceptLanguageList.charAt(t)=='=') {
-                    t++;
-                }
-                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
-                    t++;
-                }
-                try {
-                    String val = acceptLanguageList.substring(t,itemEnd).trim();
-                    q = Double.parseDouble(val);
-                } catch (NumberFormatException nfe) {
-                    q = 1.0;
-                }
-            } else {
-                q = 1.0; //default
-                paramEnd = itemEnd;
-            }
-
-            String loc = acceptLanguageList.substring(n,paramEnd).trim();
-            int serial = map.size();
-            ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q,serial);
-            map.put(entry, new ULocale(ULocale.canonicalize(loc))); // sort in reverse order..   1.0, 0.9, 0.8 .. etc
-            n = itemEnd; // get next item. (n++ will skip over delimiter)
-        }
-
-        // 2. pull out the map
-        ULocale acceptList[] = (ULocale[])map.values().toArray(new ULocale[map.size()]);
-        return acceptList;
-    }
-
     @Test
     public void TestAcceptLanguage2() {
         for(int i = 0 ; i < (ACCEPT_LANGUAGE_HTTP.length); i++) {
-            Boolean expectBoolean = new Boolean(ACCEPT_LANGUAGE_TESTS[i][1]);
+            Boolean expectBoolean = Boolean.valueOf(ACCEPT_LANGUAGE_TESTS[i][1]);
             String expectLocale=ACCEPT_LANGUAGE_TESTS[i][0];
 
             logln("#" + i + ": expecting: " + expectLocale + " (" + expectBoolean + ")");
 
             boolean r[] = { false };
-            ULocale n = ULocale.acceptLanguage(StringToULocaleArray(ACCEPT_LANGUAGE_HTTP[i]), r);
+            Set<ULocale> desiredSet =
+                    LocalePriorityList.add(ACCEPT_LANGUAGE_HTTP[i]).build().getULocales();
+            ULocale[] desiredArray = desiredSet.toArray(new ULocale[desiredSet.size()]);
+            ULocale n = ULocale.acceptLanguage(desiredArray, r);
             if((n==null)&&(expectLocale!=null)) {
-                errln("result was null! line #" + i);
+                errln("#" + i + ": result was null!");
                 continue;
             }
             if(((n==null)&&(expectLocale==null)) || (n.toString().equals(expectLocale))) {
-                logln(" locale: OK." );
+                logln("#" + i + ": locale: OK.");
             } else {
-                errln("expected " + expectLocale + " but got " + n.toString());
+                errln("#" + i + ": expected " + expectLocale + " but got " + n.toString());
             }
-            if(expectBoolean.equals(new Boolean(r[0]))) {
-                logln(" bool: OK.");
+            Boolean actualBoolean = Boolean.valueOf(r[0]);
+            if(expectBoolean.equals(actualBoolean)) {
+                logln("#" + i + ": fallback: OK.");
             } else {
-                errln("bool: not OK, was " + new Boolean(r[0]).toString() + " expected " + expectBoolean.toString());
+                errln("#" + i + ": fallback: was " + actualBoolean + " expected " + expectBoolean);
             }
         }
     }