locavailable.cpp (8468B)
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 = nullptr; 39 static int32_t availableLocaleListCount; 40 static icu::UInitOnce gInitOnceLocale {}; 41 42 namespace { 43 44 UBool U_CALLCONV locale_available_cleanup() 45 { 46 if (availableLocaleList) { 47 delete []availableLocaleList; 48 availableLocaleList = nullptr; 49 } 50 availableLocaleListCount = 0; 51 gInitOnceLocale.reset(); 52 53 return true; 54 } 55 56 } // namespace 57 58 void U_CALLCONV locale_available_init() { 59 // This function is a friend of class Locale. 60 // This function is only invoked via umtx_initOnce(). 61 62 // for now, there is a hardcoded list, so just walk through that list and set it up. 63 // Note: this function is a friend of class Locale. 64 availableLocaleListCount = uloc_countAvailable(); 65 if(availableLocaleListCount) { 66 availableLocaleList = new Locale[availableLocaleListCount]; 67 } 68 if (availableLocaleList == nullptr) { 69 availableLocaleListCount= 0; 70 } 71 for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) { 72 availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount)); 73 } 74 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup); 75 } 76 77 const Locale* U_EXPORT2 78 Locale::getAvailableLocales(int32_t& count) 79 { 80 umtx_initOnce(gInitOnceLocale, &locale_available_init); 81 count = availableLocaleListCount; 82 return availableLocaleList; 83 } 84 85 86 U_NAMESPACE_END 87 88 // C API ------------------------------------------------------------------- *** 89 90 U_NAMESPACE_USE 91 92 /* ### Constants **************************************************/ 93 94 namespace { 95 96 // Enough capacity for the two lists in the res_index.res file 97 const char** gAvailableLocaleNames[2] = {}; 98 int32_t gAvailableLocaleCounts[2] = {}; 99 icu::UInitOnce ginstalledLocalesInitOnce {}; 100 101 class AvailableLocalesSink : public ResourceSink { 102 public: 103 void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { 104 if (U_FAILURE(status)) { return; } 105 ResourceTable resIndexTable = value.getTable(status); 106 if (U_FAILURE(status)) { return; } 107 for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) { 108 ULocAvailableType type; 109 if (uprv_strcmp(key, "InstalledLocales") == 0) { 110 type = ULOC_AVAILABLE_DEFAULT; 111 } else if (uprv_strcmp(key, "AliasLocales") == 0) { 112 type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; 113 } else { 114 // CLDRVersion, etc. 115 continue; 116 } 117 ResourceTable availableLocalesTable = value.getTable(status); 118 if (U_FAILURE(status)) { 119 return; 120 } 121 gAvailableLocaleCounts[type] = availableLocalesTable.getSize(); 122 gAvailableLocaleNames[type] = static_cast<const char**>( 123 uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*))); 124 if (gAvailableLocaleNames[type] == nullptr) { 125 status = U_MEMORY_ALLOCATION_ERROR; 126 return; 127 } 128 for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) { 129 gAvailableLocaleNames[type][j] = key; 130 } 131 } 132 } 133 }; 134 135 class AvailableLocalesStringEnumeration : public StringEnumeration { 136 public: 137 AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) { 138 } 139 140 const char* next(int32_t *resultLength, UErrorCode &status) override { 141 if (U_FAILURE(status)) { return nullptr; } 142 ULocAvailableType actualType = fType; 143 int32_t actualIndex = fIndex++; 144 145 // If the "combined" list was requested, resolve that now 146 if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { 147 int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]; 148 if (actualIndex < defaultLocalesCount) { 149 actualType = ULOC_AVAILABLE_DEFAULT; 150 } else { 151 actualIndex -= defaultLocalesCount; 152 actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; 153 } 154 } 155 156 // Return the requested string 157 int32_t count = gAvailableLocaleCounts[actualType]; 158 const char* result; 159 if (actualIndex < count) { 160 result = gAvailableLocaleNames[actualType][actualIndex]; 161 if (resultLength != nullptr) { 162 *resultLength = static_cast<int32_t>(uprv_strlen(result)); 163 } 164 } else { 165 result = nullptr; 166 if (resultLength != nullptr) { 167 *resultLength = 0; 168 } 169 } 170 return result; 171 } 172 173 void reset(UErrorCode &status) override { 174 if (U_FAILURE(status)) { return; } 175 fIndex = 0; 176 } 177 178 int32_t count(UErrorCode &status) const override { 179 if (U_FAILURE(status)) { return 0; } 180 if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { 181 return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT] 182 + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES]; 183 } else { 184 return gAvailableLocaleCounts[fType]; 185 } 186 } 187 188 private: 189 ULocAvailableType fType; 190 int32_t fIndex = 0; 191 }; 192 193 /* ### Get available **************************************************/ 194 195 UBool U_CALLCONV uloc_cleanup() { 196 for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) { 197 uprv_free(gAvailableLocaleNames[i]); 198 gAvailableLocaleNames[i] = nullptr; 199 gAvailableLocaleCounts[i] = 0; 200 } 201 ginstalledLocalesInitOnce.reset(); 202 return true; 203 } 204 205 // Load Installed Locales. This function will be called exactly once 206 // via the initOnce mechanism. 207 208 void U_CALLCONV loadInstalledLocales(UErrorCode& status) { 209 ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup); 210 211 icu::LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "res_index", &status)); 212 AvailableLocalesSink sink; 213 ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status); 214 } 215 216 void _load_installedLocales(UErrorCode& status) { 217 umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status); 218 } 219 220 } // namespace 221 222 U_CAPI const char* U_EXPORT2 223 uloc_getAvailable(int32_t offset) { 224 icu::ErrorCode status; 225 _load_installedLocales(status); 226 if (status.isFailure()) { 227 return nullptr; 228 } 229 if (offset > gAvailableLocaleCounts[0]) { 230 // *status = U_ILLEGAL_ARGUMENT_ERROR; 231 return nullptr; 232 } 233 return gAvailableLocaleNames[0][offset]; 234 } 235 236 U_CAPI int32_t U_EXPORT2 237 uloc_countAvailable() { 238 icu::ErrorCode status; 239 _load_installedLocales(status); 240 if (status.isFailure()) { 241 return 0; 242 } 243 return gAvailableLocaleCounts[0]; 244 } 245 246 U_CAPI UEnumeration* U_EXPORT2 247 uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) { 248 if (U_FAILURE(*status)) { 249 return nullptr; 250 } 251 if (type < 0 || type >= ULOC_AVAILABLE_COUNT) { 252 *status = U_ILLEGAL_ARGUMENT_ERROR; 253 return nullptr; 254 } 255 _load_installedLocales(*status); 256 if (U_FAILURE(*status)) { 257 return nullptr; 258 } 259 LocalPointer<AvailableLocalesStringEnumeration> result( 260 new AvailableLocalesStringEnumeration(type), *status); 261 if (U_FAILURE(*status)) { 262 return nullptr; 263 } 264 return uenum_openFromStringEnumeration(result.orphan(), status); 265 }