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