#include "unicode/utypes.h"
#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION
#include "unicode/resbund.h"
#include "unicode/uenum.h"
#include "cmemory.h"
#include "ustrfmt.h"
#include "locutil.h"
#include "charstr.h"
#include "ucln_cmn.h"
#include "uassert.h"
#include "umutex.h"
static icu::UInitOnce LocaleUtilityInitOnce {};
static icu::Hashtable * LocaleUtility_cache = nullptr;
#define UNDERSCORE_CHAR …
#define AT_SIGN_CHAR …
#define PERIOD_CHAR …
U_CDECL_BEGIN
static UBool U_CALLCONV service_cleanup() {
if (LocaleUtility_cache) {
delete LocaleUtility_cache;
LocaleUtility_cache = nullptr;
}
return true;
}
static void U_CALLCONV locale_utility_init(UErrorCode &status) {
using namespace icu;
U_ASSERT(LocaleUtility_cache == nullptr);
ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup);
LocaleUtility_cache = new Hashtable(status);
if (U_FAILURE(status)) {
delete LocaleUtility_cache;
LocaleUtility_cache = nullptr;
return;
}
if (LocaleUtility_cache == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
LocaleUtility_cache->setValueDeleter(uhash_deleteHashtable);
}
U_CDECL_END
U_NAMESPACE_BEGIN
UnicodeString&
LocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result)
{
if (id == nullptr) {
result.setToBogus();
} else {
result = *id;
int32_t i = 0;
int32_t end = result.indexOf(AT_SIGN_CHAR);
int32_t n = result.indexOf(PERIOD_CHAR);
if (n >= 0 && n < end) {
end = n;
}
if (end < 0) {
end = result.length();
}
n = result.indexOf(UNDERSCORE_CHAR);
if (n < 0) {
n = end;
}
for (; i < n; ++i) {
char16_t c = result.charAt(i);
if (c >= 0x0041 && c <= 0x005a) {
c += 0x20;
result.setCharAt(i, c);
}
}
for (n = end; i < n; ++i) {
char16_t c = result.charAt(i);
if (c >= 0x0061 && c <= 0x007a) {
c -= 0x20;
result.setCharAt(i, c);
}
}
}
return result;
#if 0
result.setToBogus();
if (id != 0) {
int32_t buflen = id->length() + 8;
char* buf = (char*) uprv_malloc(buflen);
char* canon = (buf == 0) ? 0 : (char*) uprv_malloc(buflen);
if (buf != 0 && canon != 0) {
U_ASSERT(id->extract(0, INT32_MAX, buf, buflen) < buflen);
UErrorCode ec = U_ZERO_ERROR;
uloc_canonicalize(buf, canon, buflen, &ec);
if (U_SUCCESS(ec)) {
result = UnicodeString(canon);
}
}
uprv_free(buf);
uprv_free(canon);
}
return result;
#endif
}
Locale&
LocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result)
{
if (id.isBogus()) {
result.setToBogus();
} else {
CharString buffer;
int32_t prev, i;
prev = 0;
UErrorCode status = U_ZERO_ERROR;
do {
i = id.indexOf(static_cast<char16_t>(0x40), prev);
if(i < 0) {
buffer.appendInvariantChars(id.tempSubString(prev), status);
break;
} else {
buffer.appendInvariantChars(id.tempSubString(prev, i - prev), status);
buffer.append('@', status);
prev = i + 1;
}
} while (U_SUCCESS(status));
if (U_FAILURE(status)) {
result.setToBogus();
} else {
result = Locale::createFromName(buffer.data());
}
}
return result;
}
UnicodeString&
LocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result)
{
if (locale.isBogus()) {
result.setToBogus();
} else {
result.append(UnicodeString(locale.getName(), -1, US_INV));
}
return result;
}
const Hashtable*
LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
{
UErrorCode status = U_ZERO_ERROR;
umtx_initOnce(LocaleUtilityInitOnce, locale_utility_init, status);
Hashtable *cache = LocaleUtility_cache;
if (cache == nullptr) {
return nullptr;
}
Hashtable* htp;
umtx_lock(nullptr);
htp = static_cast<Hashtable*>(cache->get(bundleID));
umtx_unlock(nullptr);
if (htp == nullptr) {
htp = new Hashtable(status);
if (htp && U_SUCCESS(status)) {
CharString cbundleID;
cbundleID.appendInvariantChars(bundleID, status);
const char* path = cbundleID.isEmpty() ? nullptr : cbundleID.data();
icu::LocalUEnumerationPointer uenum(ures_openAvailableLocales(path, &status));
for (;;) {
const char16_t* id = uenum_unext(uenum.getAlias(), nullptr, &status);
if (id == nullptr) {
break;
}
htp->put(UnicodeString(id), (void*)htp, status);
}
if (U_FAILURE(status)) {
delete htp;
return nullptr;
}
umtx_lock(nullptr);
Hashtable *t = static_cast<Hashtable *>(cache->get(bundleID));
if (t != nullptr) {
umtx_unlock(nullptr);
delete htp;
htp = t;
} else {
cache->put(bundleID, (void*)htp, status);
umtx_unlock(nullptr);
}
}
}
return htp;
}
bool
LocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child)
{
return child.indexOf(root) == 0 &&
(child.length() == root.length() ||
child.charAt(root.length()) == UNDERSCORE_CHAR);
}
U_NAMESPACE_END
#endif