266 lines
8.3 KiB
C++
266 lines
8.3 KiB
C++
// © 2016 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
/*
|
|
*******************************************************************************
|
|
*
|
|
* Copyright (C) 1997-2013, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
*
|
|
*******************************************************************************
|
|
* file name: locavailable.cpp
|
|
* encoding: UTF-8
|
|
* tab size: 8 (not used)
|
|
* indentation:4
|
|
*
|
|
* created on: 2010feb25
|
|
* created by: Markus W. Scherer
|
|
*
|
|
* Code for available locales, separated out from other .cpp files
|
|
* that then do not depend on resource bundle code and res_index bundles.
|
|
*/
|
|
|
|
#include "unicode/errorcode.h"
|
|
#include "unicode/utypes.h"
|
|
#include "unicode/locid.h"
|
|
#include "unicode/uloc.h"
|
|
#include "unicode/ures.h"
|
|
#include "cmemory.h"
|
|
#include "cstring.h"
|
|
#include "ucln_cmn.h"
|
|
#include "uassert.h"
|
|
#include "umutex.h"
|
|
#include "uresimp.h"
|
|
|
|
// C++ API ----------------------------------------------------------------- ***
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
static icu::Locale* availableLocaleList = nullptr;
|
|
static int32_t availableLocaleListCount;
|
|
static icu::UInitOnce gInitOnceLocale {};
|
|
|
|
namespace {
|
|
|
|
UBool U_CALLCONV locale_available_cleanup()
|
|
{
|
|
if (availableLocaleList) {
|
|
delete []availableLocaleList;
|
|
availableLocaleList = nullptr;
|
|
}
|
|
availableLocaleListCount = 0;
|
|
gInitOnceLocale.reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void U_CALLCONV locale_available_init() {
|
|
// This function is a friend of class Locale.
|
|
// This function is only invoked via umtx_initOnce().
|
|
|
|
// for now, there is a hardcoded list, so just walk through that list and set it up.
|
|
// Note: this function is a friend of class Locale.
|
|
availableLocaleListCount = uloc_countAvailable();
|
|
if(availableLocaleListCount) {
|
|
availableLocaleList = new Locale[availableLocaleListCount];
|
|
}
|
|
if (availableLocaleList == nullptr) {
|
|
availableLocaleListCount= 0;
|
|
}
|
|
for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) {
|
|
availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount));
|
|
}
|
|
ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup);
|
|
}
|
|
|
|
const Locale* U_EXPORT2
|
|
Locale::getAvailableLocales(int32_t& count)
|
|
{
|
|
umtx_initOnce(gInitOnceLocale, &locale_available_init);
|
|
count = availableLocaleListCount;
|
|
return availableLocaleList;
|
|
}
|
|
|
|
|
|
U_NAMESPACE_END
|
|
|
|
// C API ------------------------------------------------------------------- ***
|
|
|
|
U_NAMESPACE_USE
|
|
|
|
/* ### Constants **************************************************/
|
|
|
|
namespace {
|
|
|
|
// Enough capacity for the two lists in the res_index.res file
|
|
const char** gAvailableLocaleNames[2] = {};
|
|
int32_t gAvailableLocaleCounts[2] = {};
|
|
icu::UInitOnce ginstalledLocalesInitOnce {};
|
|
|
|
class AvailableLocalesSink : public ResourceSink {
|
|
public:
|
|
void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
|
|
if (U_FAILURE(status)) { return; }
|
|
ResourceTable resIndexTable = value.getTable(status);
|
|
if (U_FAILURE(status)) { return; }
|
|
for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) {
|
|
ULocAvailableType type;
|
|
if (uprv_strcmp(key, "InstalledLocales") == 0) {
|
|
type = ULOC_AVAILABLE_DEFAULT;
|
|
} else if (uprv_strcmp(key, "AliasLocales") == 0) {
|
|
type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
|
|
} else {
|
|
// CLDRVersion, etc.
|
|
continue;
|
|
}
|
|
ResourceTable availableLocalesTable = value.getTable(status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
gAvailableLocaleCounts[type] = availableLocalesTable.getSize();
|
|
gAvailableLocaleNames[type] = static_cast<const char**>(
|
|
uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*)));
|
|
if (gAvailableLocaleNames[type] == nullptr) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) {
|
|
gAvailableLocaleNames[type][j] = key;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class AvailableLocalesStringEnumeration : public StringEnumeration {
|
|
public:
|
|
AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) {
|
|
}
|
|
|
|
const char* next(int32_t *resultLength, UErrorCode &status) override {
|
|
if (U_FAILURE(status)) { return nullptr; }
|
|
ULocAvailableType actualType = fType;
|
|
int32_t actualIndex = fIndex++;
|
|
|
|
// If the "combined" list was requested, resolve that now
|
|
if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
|
|
int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT];
|
|
if (actualIndex < defaultLocalesCount) {
|
|
actualType = ULOC_AVAILABLE_DEFAULT;
|
|
} else {
|
|
actualIndex -= defaultLocalesCount;
|
|
actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
|
|
}
|
|
}
|
|
|
|
// Return the requested string
|
|
int32_t count = gAvailableLocaleCounts[actualType];
|
|
const char* result;
|
|
if (actualIndex < count) {
|
|
result = gAvailableLocaleNames[actualType][actualIndex];
|
|
if (resultLength != nullptr) {
|
|
*resultLength = static_cast<int32_t>(uprv_strlen(result));
|
|
}
|
|
} else {
|
|
result = nullptr;
|
|
if (resultLength != nullptr) {
|
|
*resultLength = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void reset(UErrorCode &status) override {
|
|
if (U_FAILURE(status)) { return; }
|
|
fIndex = 0;
|
|
}
|
|
|
|
int32_t count(UErrorCode &status) const override {
|
|
if (U_FAILURE(status)) { return 0; }
|
|
if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
|
|
return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]
|
|
+ gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES];
|
|
} else {
|
|
return gAvailableLocaleCounts[fType];
|
|
}
|
|
}
|
|
|
|
private:
|
|
ULocAvailableType fType;
|
|
int32_t fIndex = 0;
|
|
};
|
|
|
|
/* ### Get available **************************************************/
|
|
|
|
UBool U_CALLCONV uloc_cleanup() {
|
|
for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) {
|
|
uprv_free(gAvailableLocaleNames[i]);
|
|
gAvailableLocaleNames[i] = nullptr;
|
|
gAvailableLocaleCounts[i] = 0;
|
|
}
|
|
ginstalledLocalesInitOnce.reset();
|
|
return true;
|
|
}
|
|
|
|
// Load Installed Locales. This function will be called exactly once
|
|
// via the initOnce mechanism.
|
|
|
|
void U_CALLCONV loadInstalledLocales(UErrorCode& status) {
|
|
ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup);
|
|
|
|
icu::LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "res_index", &status));
|
|
AvailableLocalesSink sink;
|
|
ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status);
|
|
}
|
|
|
|
void _load_installedLocales(UErrorCode& status) {
|
|
umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
U_CAPI const char* U_EXPORT2
|
|
uloc_getAvailable(int32_t offset) {
|
|
icu::ErrorCode status;
|
|
_load_installedLocales(status);
|
|
if (status.isFailure()) {
|
|
return nullptr;
|
|
}
|
|
if (offset > gAvailableLocaleCounts[0]) {
|
|
// *status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return nullptr;
|
|
}
|
|
return gAvailableLocaleNames[0][offset];
|
|
}
|
|
|
|
U_CAPI int32_t U_EXPORT2
|
|
uloc_countAvailable() {
|
|
icu::ErrorCode status;
|
|
_load_installedLocales(status);
|
|
if (status.isFailure()) {
|
|
return 0;
|
|
}
|
|
return gAvailableLocaleCounts[0];
|
|
}
|
|
|
|
U_CAPI UEnumeration* U_EXPORT2
|
|
uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) {
|
|
if (U_FAILURE(*status)) {
|
|
return nullptr;
|
|
}
|
|
if (type < 0 || type >= ULOC_AVAILABLE_COUNT) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return nullptr;
|
|
}
|
|
_load_installedLocales(*status);
|
|
if (U_FAILURE(*status)) {
|
|
return nullptr;
|
|
}
|
|
LocalPointer<AvailableLocalesStringEnumeration> result(
|
|
new AvailableLocalesStringEnumeration(type), *status);
|
|
if (U_FAILURE(*status)) {
|
|
return nullptr;
|
|
}
|
|
return uenum_openFromStringEnumeration(result.orphan(), status);
|
|
}
|