1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html |
3 | /* |
4 | ******************************************************************************* |
5 | * |
6 | * Copyright (C) 1997-2013, International Business Machines |
7 | * Corporation and others. All Rights Reserved. |
8 | * |
9 | ******************************************************************************* |
10 | * file name: locavailable.cpp |
11 | * encoding: UTF-8 |
12 | * tab size: 8 (not used) |
13 | * indentation:4 |
14 | * |
15 | * created on: 2010feb25 |
16 | * created by: Markus W. Scherer |
17 | * |
18 | * Code for available locales, separated out from other .cpp files |
19 | * that then do not depend on resource bundle code and res_index bundles. |
20 | */ |
21 | |
22 | #include "unicode/errorcode.h" |
23 | #include "unicode/utypes.h" |
24 | #include "unicode/locid.h" |
25 | #include "unicode/uloc.h" |
26 | #include "unicode/ures.h" |
27 | #include "cmemory.h" |
28 | #include "cstring.h" |
29 | #include "ucln_cmn.h" |
30 | #include "uassert.h" |
31 | #include "umutex.h" |
32 | #include "uresimp.h" |
33 | |
34 | // C++ API ----------------------------------------------------------------- *** |
35 | |
36 | U_NAMESPACE_BEGIN |
37 | |
38 | static icu::Locale* availableLocaleList = NULL; |
39 | static int32_t availableLocaleListCount; |
40 | static icu::UInitOnce gInitOnceLocale = U_INITONCE_INITIALIZER; |
41 | |
42 | U_NAMESPACE_END |
43 | |
44 | U_CDECL_BEGIN |
45 | |
46 | static UBool U_CALLCONV locale_available_cleanup(void) |
47 | { |
48 | U_NAMESPACE_USE |
49 | |
50 | if (availableLocaleList) { |
51 | delete []availableLocaleList; |
52 | availableLocaleList = NULL; |
53 | } |
54 | availableLocaleListCount = 0; |
55 | gInitOnceLocale.reset(); |
56 | |
57 | return TRUE; |
58 | } |
59 | |
60 | U_CDECL_END |
61 | |
62 | U_NAMESPACE_BEGIN |
63 | |
64 | void U_CALLCONV locale_available_init() { |
65 | // This function is a friend of class Locale. |
66 | // This function is only invoked via umtx_initOnce(). |
67 | |
68 | // for now, there is a hardcoded list, so just walk through that list and set it up. |
69 | // Note: this function is a friend of class Locale. |
70 | availableLocaleListCount = uloc_countAvailable(); |
71 | if(availableLocaleListCount) { |
72 | availableLocaleList = new Locale[availableLocaleListCount]; |
73 | } |
74 | if (availableLocaleList == NULL) { |
75 | availableLocaleListCount= 0; |
76 | } |
77 | for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) { |
78 | availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount)); |
79 | } |
80 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup); |
81 | } |
82 | |
83 | const Locale* U_EXPORT2 |
84 | Locale::getAvailableLocales(int32_t& count) |
85 | { |
86 | umtx_initOnce(gInitOnceLocale, &locale_available_init); |
87 | count = availableLocaleListCount; |
88 | return availableLocaleList; |
89 | } |
90 | |
91 | |
92 | U_NAMESPACE_END |
93 | |
94 | // C API ------------------------------------------------------------------- *** |
95 | |
96 | U_NAMESPACE_USE |
97 | |
98 | /* ### Constants **************************************************/ |
99 | |
100 | namespace { |
101 | |
102 | // Enough capacity for the two lists in the res_index.res file |
103 | const char** gAvailableLocaleNames[2] = {}; |
104 | int32_t gAvailableLocaleCounts[2] = {}; |
105 | icu::UInitOnce ginstalledLocalesInitOnce = U_INITONCE_INITIALIZER; |
106 | |
107 | class AvailableLocalesSink : public ResourceSink { |
108 | public: |
109 | void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE { |
110 | ResourceTable resIndexTable = value.getTable(status); |
111 | if (U_FAILURE(status)) { |
112 | return; |
113 | } |
114 | for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) { |
115 | ULocAvailableType type; |
116 | if (uprv_strcmp(key, "InstalledLocales" ) == 0) { |
117 | type = ULOC_AVAILABLE_DEFAULT; |
118 | } else if (uprv_strcmp(key, "AliasLocales" ) == 0) { |
119 | type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; |
120 | } else { |
121 | // CLDRVersion, etc. |
122 | continue; |
123 | } |
124 | ResourceTable availableLocalesTable = value.getTable(status); |
125 | if (U_FAILURE(status)) { |
126 | return; |
127 | } |
128 | gAvailableLocaleCounts[type] = availableLocalesTable.getSize(); |
129 | gAvailableLocaleNames[type] = static_cast<const char**>( |
130 | uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*))); |
131 | if (gAvailableLocaleNames[type] == nullptr) { |
132 | status = U_MEMORY_ALLOCATION_ERROR; |
133 | return; |
134 | } |
135 | for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) { |
136 | gAvailableLocaleNames[type][j] = key; |
137 | } |
138 | } |
139 | } |
140 | }; |
141 | |
142 | class AvailableLocalesStringEnumeration : public StringEnumeration { |
143 | public: |
144 | AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) { |
145 | } |
146 | |
147 | const char* next(int32_t *resultLength, UErrorCode&) override { |
148 | ULocAvailableType actualType = fType; |
149 | int32_t actualIndex = fIndex++; |
150 | |
151 | // If the "combined" list was requested, resolve that now |
152 | if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { |
153 | int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]; |
154 | if (actualIndex < defaultLocalesCount) { |
155 | actualType = ULOC_AVAILABLE_DEFAULT; |
156 | } else { |
157 | actualIndex -= defaultLocalesCount; |
158 | actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; |
159 | } |
160 | } |
161 | |
162 | // Return the requested string |
163 | int32_t count = gAvailableLocaleCounts[actualType]; |
164 | const char* result; |
165 | if (actualIndex < count) { |
166 | result = gAvailableLocaleNames[actualType][actualIndex]; |
167 | if (resultLength != nullptr) { |
168 | *resultLength = static_cast<int32_t>(uprv_strlen(result)); |
169 | } |
170 | } else { |
171 | result = nullptr; |
172 | if (resultLength != nullptr) { |
173 | *resultLength = 0; |
174 | } |
175 | } |
176 | return result; |
177 | } |
178 | |
179 | void reset(UErrorCode&) override { |
180 | fIndex = 0; |
181 | } |
182 | |
183 | int32_t count(UErrorCode&) const override { |
184 | if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { |
185 | return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT] |
186 | + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES]; |
187 | } else { |
188 | return gAvailableLocaleCounts[fType]; |
189 | } |
190 | } |
191 | |
192 | private: |
193 | ULocAvailableType fType; |
194 | int32_t fIndex = 0; |
195 | }; |
196 | |
197 | /* ### Get available **************************************************/ |
198 | |
199 | static UBool U_CALLCONV uloc_cleanup(void) { |
200 | for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) { |
201 | uprv_free(gAvailableLocaleNames[i]); |
202 | gAvailableLocaleNames[i] = nullptr; |
203 | gAvailableLocaleCounts[i] = 0; |
204 | } |
205 | ginstalledLocalesInitOnce.reset(); |
206 | return TRUE; |
207 | } |
208 | |
209 | // Load Installed Locales. This function will be called exactly once |
210 | // via the initOnce mechanism. |
211 | |
212 | static void U_CALLCONV loadInstalledLocales(UErrorCode& status) { |
213 | ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup); |
214 | |
215 | icu::LocalUResourceBundlePointer rb(ures_openDirect(NULL, "res_index" , &status)); |
216 | AvailableLocalesSink sink; |
217 | ures_getAllItemsWithFallback(rb.getAlias(), "" , sink, status); |
218 | } |
219 | |
220 | void _load_installedLocales(UErrorCode& status) { |
221 | umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status); |
222 | } |
223 | |
224 | } // namespace |
225 | |
226 | U_CAPI const char* U_EXPORT2 |
227 | uloc_getAvailable(int32_t offset) { |
228 | icu::ErrorCode status; |
229 | _load_installedLocales(status); |
230 | if (status.isFailure()) { |
231 | return nullptr; |
232 | } |
233 | if (offset > gAvailableLocaleCounts[0]) { |
234 | // *status = U_ILLEGAL_ARGUMENT_ERROR; |
235 | return nullptr; |
236 | } |
237 | return gAvailableLocaleNames[0][offset]; |
238 | } |
239 | |
240 | U_CAPI int32_t U_EXPORT2 |
241 | uloc_countAvailable() { |
242 | icu::ErrorCode status; |
243 | _load_installedLocales(status); |
244 | if (status.isFailure()) { |
245 | return 0; |
246 | } |
247 | return gAvailableLocaleCounts[0]; |
248 | } |
249 | |
250 | U_CAPI UEnumeration* U_EXPORT2 |
251 | uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) { |
252 | if (U_FAILURE(*status)) { |
253 | return nullptr; |
254 | } |
255 | if (type < 0 || type >= ULOC_AVAILABLE_COUNT) { |
256 | *status = U_ILLEGAL_ARGUMENT_ERROR; |
257 | return nullptr; |
258 | } |
259 | _load_installedLocales(*status); |
260 | if (U_FAILURE(*status)) { |
261 | return nullptr; |
262 | } |
263 | LocalPointer<AvailableLocalesStringEnumeration> result( |
264 | new AvailableLocalesStringEnumeration(type), *status); |
265 | if (U_FAILURE(*status)) { |
266 | return nullptr; |
267 | } |
268 | return uenum_openFromStringEnumeration(result.orphan(), status); |
269 | } |
270 | |
271 | |