183a171d1a62abf406f7f44ae671823d5ec20db7dCraig Cornelius/*
2ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru *******************************************************************************
3f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius * Copyright (C) 2002-2014, International Business Machines Corporation and
427f654740f2a26ad62a5c155af9199af9e69b889claireho * others. All Rights Reserved.
5ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru *******************************************************************************
6ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */
7ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "unicode/utypes.h"
8ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
9ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION
10ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
11ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "unicode/resbund.h"
12ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "cmemory.h"
13ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "ustrfmt.h"
14ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "locutil.h"
15ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "charstr.h"
16ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "ucln_cmn.h"
17ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "uassert.h"
18ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#include "umutex.h"
19ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
20ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru// see LocaleUtility::getAvailableLocaleNames
21f9878a236aa0d9662d8e40cafdaf2e04cd615835ccorneliusstatic icu::UInitOnce   LocaleUtilityInitOnce = U_INITONCE_INITIALIZER;
2283a171d1a62abf406f7f44ae671823d5ec20db7dCraig Corneliusstatic icu::Hashtable * LocaleUtility_cache = NULL;
23ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
24ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#define UNDERSCORE_CHAR ((UChar)0x005f)
25ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#define AT_SIGN_CHAR    ((UChar)64)
26ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#define PERIOD_CHAR     ((UChar)46)
27ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
28ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru/*
29ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru ******************************************************************
30ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */
31ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
32ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru/**
33ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru * Release all static memory held by Locale Utility.
34ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru */
35ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruU_CDECL_BEGIN
36ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Querustatic UBool U_CALLCONV service_cleanup(void) {
37ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (LocaleUtility_cache) {
38ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        delete LocaleUtility_cache;
39ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        LocaleUtility_cache = NULL;
40ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
41ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return TRUE;
42ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
43f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius
44f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius
45f9878a236aa0d9662d8e40cafdaf2e04cd615835ccorneliusstatic void U_CALLCONV locale_utility_init(UErrorCode &status) {
46f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    using namespace icu;
47f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    U_ASSERT(LocaleUtility_cache == NULL);
48f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup);
49f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    LocaleUtility_cache = new Hashtable(status);
50f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    if (U_FAILURE(status)) {
51f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        delete LocaleUtility_cache;
52f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        LocaleUtility_cache = NULL;
53f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        return;
54f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    }
55f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    if (LocaleUtility_cache == NULL) {
56f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        status = U_MEMORY_ALLOCATION_ERROR;
57f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        return;
58f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    }
59f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    LocaleUtility_cache->setValueDeleter(uhash_deleteHashtable);
60f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius}
61f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius
62ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruU_CDECL_END
63ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
64ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruU_NAMESPACE_BEGIN
65ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
66ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruUnicodeString&
67ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result)
68ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru{
69ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru  if (id == NULL) {
70ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    result.setToBogus();
71ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru  } else {
72ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // Fix case only (no other changes) up to the first '@' or '.' or
73ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // end of string, whichever comes first.  In 3.0 I changed this to
74ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // stop at first '@' or '.'.  It used to run out to the end of
75ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // string.  My fix makes the tests pass but is probably
76ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // structurally incorrect.  See below.  [alan 3.0]
77ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
78ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // TODO: Doug, you might want to revise this...
79ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    result = *id;
80ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    int32_t i = 0;
81ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    int32_t end = result.indexOf(AT_SIGN_CHAR);
82ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    int32_t n = result.indexOf(PERIOD_CHAR);
83ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (n >= 0 && n < end) {
84ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        end = n;
85ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
86ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (end < 0) {
87ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        end = result.length();
88ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
89ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    n = result.indexOf(UNDERSCORE_CHAR);
90ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (n < 0) {
91ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      n = end;
92ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
93ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    for (; i < n; ++i) {
94ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      UChar c = result.charAt(i);
95ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      if (c >= 0x0041 && c <= 0x005a) {
96ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        c += 0x20;
97ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result.setCharAt(i, c);
98ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      }
99ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
100ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    for (n = end; i < n; ++i) {
101ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      UChar c = result.charAt(i);
102ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      if (c >= 0x0061 && c <= 0x007a) {
103ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        c -= 0x20;
104ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result.setCharAt(i, c);
105ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      }
106ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
107ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru  }
108ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru  return result;
109ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
110ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#if 0
111ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // This code does a proper full level 2 canonicalization of id.
112ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // It's nasty to go from UChar to char to char to UChar -- but
113ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // that's what you have to do to use the uloc_canonicalize
114ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // function on UnicodeStrings.
115ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
116ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // I ended up doing the alternate fix (see above) not for
117ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // performance reasons, although performance will certainly be
118ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // better, but because doing a full level 2 canonicalization
119ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // causes some tests to fail.  [alan 3.0]
120ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
121ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // TODO: Doug, you might want to revisit this...
122ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    result.setToBogus();
123ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (id != 0) {
124ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        int32_t buflen = id->length() + 8; // space for NUL
125ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        char* buf = (char*) uprv_malloc(buflen);
126ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        char* canon = (buf == 0) ? 0 : (char*) uprv_malloc(buflen);
127ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        if (buf != 0 && canon != 0) {
128ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            U_ASSERT(id->extract(0, INT32_MAX, buf, buflen) < buflen);
129ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            UErrorCode ec = U_ZERO_ERROR;
130ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            uloc_canonicalize(buf, canon, buflen, &ec);
131ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            if (U_SUCCESS(ec)) {
132ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                result = UnicodeString(canon);
133ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            }
134ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        }
135ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        uprv_free(buf);
136ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        uprv_free(canon);
137ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
138ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return result;
139ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#endif
140ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
141ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
142ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocale&
143ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result)
144ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru{
145ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    enum { BUFLEN = 128 }; // larger than ever needed
146ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
147ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (id.isBogus() || id.length() >= BUFLEN) {
148ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result.setToBogus();
149ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    } else {
150ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        /*
151ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * We need to convert from a UnicodeString to char * in order to
152ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * create a Locale.
153ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         *
154ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * Problem: Locale ID strings may contain '@' which is a variant
155ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * character and cannot be handled by invariant-character conversion.
156ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         *
157ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * Hack: Since ICU code can handle locale IDs with multiple encodings
158ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * of '@' (at least for EBCDIC; it's not known to be a problem for
159ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * ASCII-based systems),
160ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * we use regular invariant-character conversion for everything else
161ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * and manually convert U+0040 into a compiler-char-constant '@'.
162ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * While this compilation-time constant may not match the runtime
163ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * encoding of '@', it should be one of the encodings which ICU
164ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * recognizes.
165ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         *
166ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         * There should be only at most one '@' in a locale ID.
167ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru         */
168ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        char buffer[BUFLEN];
169ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        int32_t prev, i;
170ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        prev = 0;
171ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        for(;;) {
172ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            i = id.indexOf((UChar)0x40, prev);
173ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            if(i < 0) {
174ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                // no @ between prev and the rest of the string
175ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                id.extract(prev, INT32_MAX, buffer + prev, BUFLEN - prev, US_INV);
176ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                break; // done
177ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            } else {
178ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                // normal invariant-character conversion for text between @s
179ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                id.extract(prev, i - prev, buffer + prev, BUFLEN - prev, US_INV);
180ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                // manually "convert" U+0040 at id[i] into '@' at buffer[i]
181ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                buffer[i] = '@';
182ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                prev = i + 1;
183ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            }
184ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        }
185ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result = Locale::createFromName(buffer);
186ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
187ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return result;
188ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
189ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
190ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruUnicodeString&
191ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result)
192ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru{
193ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (locale.isBogus()) {
194ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result.setToBogus();
195ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    } else {
196ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        result.append(UnicodeString(locale.getName(), -1, US_INV));
197ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
198ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return result;
199ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
200ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
201ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queruconst Hashtable*
202ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
203ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru{
204ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // LocaleUtility_cache is a hash-of-hashes.  The top-level keys
205ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // are path strings ('bundleID') passed to
206ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // ures_openAvailableLocales.  The top-level values are
207ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // second-level hashes.  The second-level keys are result strings
208ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // from ures_openAvailableLocales.  The second-level values are
209ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    // garbage ((void*)1 or other random pointer).
210ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
211ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    UErrorCode status = U_ZERO_ERROR;
212f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    umtx_initOnce(LocaleUtilityInitOnce, locale_utility_init, status);
213f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius    Hashtable *cache = LocaleUtility_cache;
214ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (cache == NULL) {
215f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        // Catastrophic failure.
216f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius        return NULL;
217ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
218ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
219ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    Hashtable* htp;
220ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    umtx_lock(NULL);
221ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    htp = (Hashtable*) cache->get(bundleID);
222ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    umtx_unlock(NULL);
223ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
224ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    if (htp == NULL) {
225ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        htp = new Hashtable(status);
226ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        if (htp && U_SUCCESS(status)) {
22727f654740f2a26ad62a5c155af9199af9e69b889claireho            CharString cbundleID;
22827f654740f2a26ad62a5c155af9199af9e69b889claireho            cbundleID.appendInvariantChars(bundleID, status);
22927f654740f2a26ad62a5c155af9199af9e69b889claireho            const char* path = cbundleID.isEmpty() ? NULL : cbundleID.data();
230ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            UEnumeration *uenum = ures_openAvailableLocales(path, &status);
231ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            for (;;) {
232ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                const UChar* id = uenum_unext(uenum, NULL, &status);
233ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                if (id == NULL) {
234ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                    break;
235ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                }
236ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                htp->put(UnicodeString(id), (void*)htp, status);
237ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            }
238ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            uenum_close(uenum);
239ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            if (U_FAILURE(status)) {
240ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                delete htp;
241ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru                return NULL;
242ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            }
243ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru            umtx_lock(NULL);
244f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius            Hashtable *t = static_cast<Hashtable *>(cache->get(bundleID));
245f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius            if (t != NULL) {
246f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                // Another thread raced through this code, creating the cache entry first.
247f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                // Discard ours and return theirs.
248f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                umtx_unlock(NULL);
249f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                delete htp;
250f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                htp = t;
251f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius            } else {
252f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                cache->put(bundleID, (void*)htp, status);
253f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius                umtx_unlock(NULL);
254f9878a236aa0d9662d8e40cafdaf2e04cd615835ccornelius            }
255ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru        }
256ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    }
257ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return htp;
258ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
259ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
260ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruUBool
261ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruLocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child)
262ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru{
263ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru    return child.indexOf(root) == 0 &&
264ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru      (child.length() == root.length() ||
265ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru       child.charAt(root.length()) == UNDERSCORE_CHAR);
266ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru}
267ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
268ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste QueruU_NAMESPACE_END
269ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
270ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru/* !UCONFIG_NO_SERVICE */
271ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru#endif
272ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
273ac04d0bbe12b3ef54518635711412f178cb4d16Jean-Baptiste Queru
274