ICU-20328 Implement LocaleBuilder

Design Doc: https://goo.gl/Qf12p3
This commit is contained in:
Frank Tang 2018-10-23 09:17:55 +08:00 committed by Frank Yung-Fong Tang
parent 0e6a6f6fff
commit 6942013a38
16 changed files with 2728 additions and 104 deletions

View file

@ -88,6 +88,7 @@ ucnv_u7.o ucnv_u8.o ucnv_u16.o ucnv_u32.o ucnvscsu.o ucnvbocu.o \
ucnv_ext.o ucnvmbcs.o ucnv2022.o ucnvhz.o ucnv_lmb.o ucnvisci.o ucnvdisp.o ucnv_set.o ucnv_ct.o \
resource.o uresbund.o ures_cnv.o uresdata.o resbund.o resbund_cnv.o \
ucurr.o \
localebuilder.o \
messagepattern.o ucat.o locmap.o uloc.o locid.o locutil.o locavailable.o locdispnames.o locdspnm.o loclikely.o locresdata.o \
bytestream.o stringpiece.o bytesinkutil.o \
stringtriebuilder.o bytestriebuilder.o \

View file

@ -256,6 +256,7 @@
<ClCompile Include="uresdata.cpp" />
<ClCompile Include="resource.cpp" />
<ClCompile Include="ucurr.cpp" />
<ClCompile Include="localebuilder.cpp" />
<ClCompile Include="caniter.cpp" />
<ClCompile Include="filterednormalizer2.cpp" />
<ClCompile Include="loadednormalizer2impl.cpp" />
@ -445,6 +446,7 @@
<ClInclude Include="ustr_imp.h" />
<ClInclude Include="static_unicode_sets.h" />
<ClInclude Include="capi_helper.h" />
<ClInclude Include="unicode\localebuilder.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="common.rc" />

View file

@ -361,6 +361,9 @@
<ClCompile Include="resource.cpp">
<Filter>locales &amp; resources</Filter>
</ClCompile>
<ClCompile Include="localebuilder.cpp">
<Filter>locales &amp; resources</Filter>
</ClCompile>
<ClCompile Include="caniter.cpp">
<Filter>normalization</Filter>
</ClCompile>
@ -1225,5 +1228,8 @@
<CustomBuild Include="unicode\stringoptions.h">
<Filter>strings</Filter>
</CustomBuild>
<CustomBuild Include="unicode\localebuilder.h">
<Filter>locales &amp; resources</Filter>
</CustomBuild>
</ItemGroup>
</Project>

View file

@ -383,6 +383,7 @@
<ClCompile Include="uresdata.cpp" />
<ClCompile Include="resource.cpp" />
<ClCompile Include="ucurr.cpp" />
<ClCompile Include="localebuilder.cpp" />
<ClCompile Include="caniter.cpp" />
<ClCompile Include="filterednormalizer2.cpp" />
<ClCompile Include="loadednormalizer2impl.cpp" />
@ -572,6 +573,7 @@
<ClInclude Include="ustr_imp.h" />
<ClInclude Include="static_unicode_sets.h" />
<ClInclude Include="capi_helper.h" />
<ClInclude Include="unicode\localebuilder.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="common.rc" />

View file

@ -0,0 +1,436 @@
// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include <utility>
#include "bytesinkutil.h" // CharStringByteSink
#include "charstr.h"
#include "cstring.h"
#include "ulocimp.h"
#include "unicode/localebuilder.h"
#include "unicode/locid.h"
U_NAMESPACE_BEGIN
#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) )
const char* kAttributeKey = "attribute";
static bool _isExtensionSubtags(char key, const char* s, int32_t len) {
switch (uprv_tolower(key)) {
case 'u':
return ultag_isUnicodeExtensionSubtags(s, len);
case 't':
return ultag_isTransformedExtensionSubtags(s, len);
case 'x':
return ultag_isPrivateuseValueSubtags(s, len);
default:
return ultag_isExtensionSubtags(s, len);
}
}
LocaleBuilder::LocaleBuilder() : UObject(), status_(U_ZERO_ERROR), language_(),
script_(), region_(), variant_(nullptr), extensions_(nullptr)
{
language_[0] = 0;
script_[0] = 0;
region_[0] = 0;
}
LocaleBuilder::~LocaleBuilder()
{
delete variant_;
delete extensions_;
}
LocaleBuilder& LocaleBuilder::setLocale(const Locale& locale)
{
clear();
setLanguage(locale.getLanguage());
setScript(locale.getScript());
setRegion(locale.getCountry());
setVariant(locale.getVariant());
extensions_ = locale.clone();
if (extensions_ == nullptr) {
status_ = U_MEMORY_ALLOCATION_ERROR;
}
return *this;
}
LocaleBuilder& LocaleBuilder::setLanguageTag(StringPiece tag)
{
Locale l = Locale::forLanguageTag(tag, status_);
if (U_FAILURE(status_)) { return *this; }
// Because setLocale will reset status_ we need to return
// first if we have error in forLanguageTag.
setLocale(l);
return *this;
}
static void setField(StringPiece input, char* dest, UErrorCode& errorCode,
UBool (*test)(const char*, int32_t)) {
if (U_FAILURE(errorCode)) { return; }
if (input.empty()) {
dest[0] = '\0';
} else if (test(input.data(), input.length())) {
uprv_memcpy(dest, input.data(), input.length());
dest[input.length()] = '\0';
} else {
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
}
}
LocaleBuilder& LocaleBuilder::setLanguage(StringPiece language)
{
setField(language, language_, status_, &ultag_isLanguageSubtag);
return *this;
}
LocaleBuilder& LocaleBuilder::setScript(StringPiece script)
{
setField(script, script_, status_, &ultag_isScriptSubtag);
return *this;
}
LocaleBuilder& LocaleBuilder::setRegion(StringPiece region)
{
setField(region, region_, status_, &ultag_isRegionSubtag);
return *this;
}
static void transform(char* data, int32_t len) {
for (int32_t i = 0; i < len; i++, data++) {
if (*data == '_') {
*data = '-';
} else {
*data = uprv_tolower(*data);
}
}
}
LocaleBuilder& LocaleBuilder::setVariant(StringPiece variant)
{
if (U_FAILURE(status_)) { return *this; }
if (variant.empty()) {
delete variant_;
variant_ = nullptr;
return *this;
}
CharString* new_variant = new CharString(variant, status_);
if (U_FAILURE(status_)) { return *this; }
if (new_variant == nullptr) {
status_ = U_MEMORY_ALLOCATION_ERROR;
return *this;
}
transform(new_variant->data(), new_variant->length());
if (!ultag_isVariantSubtags(new_variant->data(), new_variant->length())) {
delete new_variant;
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
delete variant_;
variant_ = new_variant;
return *this;
}
static bool
_isKeywordValue(const char* key, const char* value, int32_t value_len)
{
if (key[1] == '\0') {
// one char key
return (UPRV_ISALPHANUM(uprv_tolower(key[0])) &&
_isExtensionSubtags(key[0], value, value_len));
} else if (uprv_strcmp(key, kAttributeKey) == 0) {
// unicode attributes
return ultag_isUnicodeLocaleAttributes(value, value_len);
}
// otherwise: unicode extension value
// We need to convert from legacy key/value to unicode
// key/value
const char* unicode_locale_key = uloc_toUnicodeLocaleKey(key);
const char* unicode_locale_type = uloc_toUnicodeLocaleType(key, value);
return unicode_locale_key && unicode_locale_type &&
ultag_isUnicodeLocaleKey(unicode_locale_key, -1) &&
ultag_isUnicodeLocaleType(unicode_locale_type, -1);
}
static void
_copyExtensions(const Locale& from, Locale* to, bool validate, UErrorCode& errorCode)
{
if (U_FAILURE(errorCode)) { return; }
LocalPointer<icu::StringEnumeration> iter(from.createKeywords(errorCode));
if (U_FAILURE(errorCode) || iter.isNull()) { return; }
const char* key;
while ((key = iter->next(nullptr, errorCode)) != nullptr) {
CharString value;
CharStringByteSink sink(&value);
from.getKeywordValue(key, sink, errorCode);
if (U_FAILURE(errorCode)) { return; }
if (uprv_strcmp(key, kAttributeKey) == 0) {
transform(value.data(), value.length());
}
if (validate &&
!_isKeywordValue(key, value.data(), value.length())) {
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
to->setKeywordValue(key, value.data(), errorCode);
if (U_FAILURE(errorCode)) { return; }
}
}
void static
_clearUAttributesAndKeyType(Locale* locale, UErrorCode& errorCode)
{
// Clear Unicode attributes
locale->setKeywordValue(kAttributeKey, "", errorCode);
// Clear all Unicode keyword values
LocalPointer<icu::StringEnumeration> iter(locale->createUnicodeKeywords(errorCode));
if (U_FAILURE(errorCode) || iter.isNull()) { return; }
const char* key;
while ((key = iter->next(nullptr, errorCode)) != nullptr) {
locale->setUnicodeKeywordValue(key, nullptr, errorCode);
}
}
static void
_setUnicodeExtensions(Locale* locale, const CharString& value, UErrorCode& errorCode)
{
// Add the unicode extensions to extensions_
CharString locale_str("und-u-", errorCode);
locale_str.append(value, errorCode);
_copyExtensions(
Locale::forLanguageTag(locale_str.data(), errorCode),
locale, false, errorCode);
}
LocaleBuilder& LocaleBuilder::setExtension(char key, StringPiece value)
{
if (U_FAILURE(status_)) { return *this; }
if (!UPRV_ISALPHANUM(key)) {
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
CharString value_str(value, status_);
if (U_FAILURE(status_)) { return *this; }
transform(value_str.data(), value_str.length());
if (!value_str.isEmpty() &&
!_isExtensionSubtags(key, value_str.data(), value_str.length())) {
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
if (extensions_ == nullptr) {
extensions_ = new Locale();
if (extensions_ == nullptr) {
status_ = U_MEMORY_ALLOCATION_ERROR;
return *this;
}
}
if (uprv_tolower(key) != 'u') {
// for t, x and others extension.
extensions_->setKeywordValue(StringPiece(&key, 1), value_str.data(),
status_);
return *this;
}
_clearUAttributesAndKeyType(extensions_, status_);
if (U_FAILURE(status_)) { return *this; }
if (!value.empty()) {
_setUnicodeExtensions(extensions_, value_str, status_);
}
return *this;
}
LocaleBuilder& LocaleBuilder::setUnicodeLocaleKeyword(
StringPiece key, StringPiece type)
{
if (U_FAILURE(status_)) { return *this; }
if (!ultag_isUnicodeLocaleKey(key.data(), key.length()) ||
(!type.empty() &&
!ultag_isUnicodeLocaleType(type.data(), type.length()))) {
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
if (extensions_ == nullptr) {
extensions_ = new Locale();
}
if (extensions_ == nullptr) {
status_ = U_MEMORY_ALLOCATION_ERROR;
return *this;
}
extensions_->setUnicodeKeywordValue(key, type, status_);
return *this;
}
LocaleBuilder& LocaleBuilder::addUnicodeLocaleAttribute(
StringPiece value)
{
CharString value_str(value, status_);
if (U_FAILURE(status_)) { return *this; }
transform(value_str.data(), value_str.length());
if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
if (extensions_ == nullptr) {
extensions_ = new Locale();
if (extensions_ == nullptr) {
status_ = U_MEMORY_ALLOCATION_ERROR;
return *this;
}
extensions_->setKeywordValue(kAttributeKey, value_str.data(), status_);
return *this;
}
CharString attributes;
CharStringByteSink sink(&attributes);
UErrorCode localErrorCode = U_ZERO_ERROR;
extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
if (U_FAILURE(localErrorCode)) {
CharString new_attributes(value_str.data(), status_);
// No attributes, set the attribute.
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
return *this;
}
transform(attributes.data(),attributes.length());
const char* start = attributes.data();
const char* limit = attributes.data() + attributes.length();
CharString new_attributes;
bool inserted = false;
while (start < limit) {
if (!inserted) {
int cmp = uprv_strcmp(start, value_str.data());
if (cmp == 0) { return *this; } // Found it in attributes: Just return
if (cmp > 0) {
if (!new_attributes.isEmpty()) new_attributes.append('_', status_);
new_attributes.append(value_str.data(), status_);
inserted = true;
}
}
if (!new_attributes.isEmpty()) {
new_attributes.append('_', status_);
}
new_attributes.append(start, status_);
start += uprv_strlen(start) + 1;
}
if (!inserted) {
if (!new_attributes.isEmpty()) {
new_attributes.append('_', status_);
}
new_attributes.append(value_str.data(), status_);
}
// Not yet in the attributes, set the attribute.
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
return *this;
}
LocaleBuilder& LocaleBuilder::removeUnicodeLocaleAttribute(
StringPiece value)
{
CharString value_str(value, status_);
if (U_FAILURE(status_)) { return *this; }
transform(value_str.data(), value_str.length());
if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
status_ = U_ILLEGAL_ARGUMENT_ERROR;
return *this;
}
if (extensions_ == nullptr) { return *this; }
UErrorCode localErrorCode = U_ZERO_ERROR;
CharString attributes;
CharStringByteSink sink(&attributes);
extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
// get failure, just return
if (U_FAILURE(localErrorCode)) { return *this; }
// Do not have any attributes, just return.
if (attributes.isEmpty()) { return *this; }
char* p = attributes.data();
// Replace null terminiator in place for _ and - so later
// we can use uprv_strcmp to compare.
for (int32_t i = 0; i < attributes.length(); i++, p++) {
*p = (*p == '_' || *p == '-') ? '\0' : uprv_tolower(*p);
}
const char* start = attributes.data();
const char* limit = attributes.data() + attributes.length();
CharString new_attributes;
bool found = false;
while (start < limit) {
if (uprv_strcmp(start, value_str.data()) == 0) {
found = true;
} else {
if (!new_attributes.isEmpty()) {
new_attributes.append('_', status_);
}
new_attributes.append(start, status_);
}
start += uprv_strlen(start) + 1;
}
// Found the value in attributes, set the attribute.
if (found) {
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
}
return *this;
}
LocaleBuilder& LocaleBuilder::clear()
{
status_ = U_ZERO_ERROR;
language_[0] = 0;
script_[0] = 0;
region_[0] = 0;
delete variant_;
variant_ = nullptr;
clearExtensions();
return *this;
}
LocaleBuilder& LocaleBuilder::clearExtensions()
{
delete extensions_;
extensions_ = nullptr;
return *this;
}
Locale makeBogusLocale() {
Locale bogus;
bogus.setToBogus();
return bogus;
}
Locale LocaleBuilder::build(UErrorCode& errorCode)
{
if (U_FAILURE(errorCode)) {
return makeBogusLocale();
}
if (U_FAILURE(status_)) {
errorCode = status_;
return makeBogusLocale();
}
CharString locale_str(language_, errorCode);
if (uprv_strlen(script_) > 0) {
locale_str.append('-', errorCode).append(StringPiece(script_), errorCode);
}
if (uprv_strlen(region_) > 0) {
locale_str.append('-', errorCode).append(StringPiece(region_), errorCode);
}
if (variant_ != nullptr) {
locale_str.append('-', errorCode).append(StringPiece(variant_->data()), errorCode);
}
if (U_FAILURE(errorCode)) {
return makeBogusLocale();
}
Locale product(locale_str.data());
if (extensions_ != nullptr) {
_copyExtensions(*extensions_, &product, true, errorCode);
}
if (U_FAILURE(errorCode)) {
return makeBogusLocale();
}
return product;
}
U_NAMESPACE_END

View file

@ -406,13 +406,22 @@ _isAlphaNumericString(const char* s, int32_t len) {
}
static UBool
_isLanguageSubtag(const char* s, int32_t len) {
_isAlphaNumericStringLimitedLength(const char* s, int32_t len, int32_t min, int32_t max) {
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len >= min && len <= max && _isAlphaNumericString(s, len)) {
return TRUE;
}
return FALSE;
}
U_CFUNC UBool
ultag_isLanguageSubtag(const char* s, int32_t len) {
/*
* language = 2*3ALPHA ; shortest ISO 639 code
* ["-" extlang] ; sometimes followed by
* ; extended language subtags
* / 4ALPHA ; or reserved for future use
* / 5*8ALPHA ; or registered language subtag
* unicode_language_subtag = alpha{2,3} | alpha{5,8};
* NOTE: Per ICUTC 2019/01/23- accepting alpha 4
* See ICU-20372
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
@ -438,8 +447,8 @@ _isExtlangSubtag(const char* s, int32_t len) {
return FALSE;
}
static UBool
_isScriptSubtag(const char* s, int32_t len) {
U_CFUNC UBool
ultag_isScriptSubtag(const char* s, int32_t len) {
/*
* script = 4ALPHA ; ISO 15924 code
*/
@ -452,8 +461,8 @@ _isScriptSubtag(const char* s, int32_t len) {
return FALSE;
}
static UBool
_isRegionSubtag(const char* s, int32_t len) {
U_CFUNC UBool
ultag_isRegionSubtag(const char* s, int32_t len) {
/*
* region = 2ALPHA ; ISO 3166-1 code
* / 3DIGIT ; UN M.49 code
@ -479,7 +488,7 @@ _isVariantSubtag(const char* s, int32_t len) {
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len >= 5 && len <= 8 && _isAlphaNumericString(s, len)) {
if (_isAlphaNumericStringLimitedLength(s, len, 5, 8)) {
return TRUE;
}
if (len == 4 && ISNUMERIC(*s) && _isAlphaNumericString(s + 1, 3)) {
@ -488,19 +497,48 @@ _isVariantSubtag(const char* s, int32_t len) {
return FALSE;
}
static UBool
_isSepListOf(UBool (*test)(const char*, int32_t), const char* s, int32_t len) {
const char *p = s;
const char *pSubtag = NULL;
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
while ((p - s) < len) {
if (*p == SEP) {
if (pSubtag == NULL) {
return FALSE;
}
if (!test(pSubtag, (int32_t)(p - pSubtag))) {
return FALSE;
}
pSubtag = NULL;
} else if (pSubtag == NULL) {
pSubtag = p;
}
p++;
}
if (pSubtag == NULL) {
return FALSE;
}
return test(pSubtag, (int32_t)(p - pSubtag));
}
U_CFUNC UBool
ultag_isVariantSubtags(const char* s, int32_t len) {
return _isSepListOf(&_isVariantSubtag, s, len);
}
// This is for the ICU-specific "lvariant" handling.
static UBool
_isPrivateuseVariantSubtag(const char* s, int32_t len) {
/*
* variant = 1*8alphanum ; registered variants
* / (DIGIT 3alphanum)
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
return TRUE;
}
return FALSE;
return _isAlphaNumericStringLimitedLength(s, len , 1, 8);
}
static UBool
@ -528,42 +566,12 @@ _isExtensionSubtag(const char* s, int32_t len) {
/*
* extension = singleton 1*("-" (2*8alphanum))
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len >= 2 && len <= 8 && _isAlphaNumericString(s, len)) {
return TRUE;
}
return FALSE;
return _isAlphaNumericStringLimitedLength(s, len, 2, 8);
}
static UBool
_isExtensionSubtags(const char* s, int32_t len) {
const char *p = s;
const char *pSubtag = NULL;
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
while ((p - s) < len) {
if (*p == SEP) {
if (pSubtag == NULL) {
return FALSE;
}
if (!_isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag))) {
return FALSE;
}
pSubtag = NULL;
} else if (pSubtag == NULL) {
pSubtag = p;
}
p++;
}
if (pSubtag == NULL) {
return FALSE;
}
return _isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag));
U_CFUNC UBool
ultag_isExtensionSubtags(const char* s, int32_t len) {
return _isSepListOf(&_isExtensionSubtag, s, len);
}
static UBool
@ -571,46 +579,32 @@ _isPrivateuseValueSubtag(const char* s, int32_t len) {
/*
* privateuse = "x" 1*("-" (1*8alphanum))
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
return TRUE;
}
return FALSE;
return _isAlphaNumericStringLimitedLength(s, len, 1, 8);
}
static UBool
_isPrivateuseValueSubtags(const char* s, int32_t len) {
const char *p = s;
const char *pSubtag = NULL;
U_CFUNC UBool
ultag_isPrivateuseValueSubtags(const char* s, int32_t len) {
return _isSepListOf(&_isPrivateuseValueSubtag, s, len);
}
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
U_CFUNC UBool
ultag_isUnicodeLocaleAttribute(const char* s, int32_t len) {
/*
* attribute = alphanum{3,8} ;
*/
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
}
while ((p - s) < len) {
if (*p == SEP) {
if (pSubtag == NULL) {
return FALSE;
}
if (!_isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag))) {
return FALSE;
}
pSubtag = NULL;
} else if (pSubtag == NULL) {
pSubtag = p;
}
p++;
}
if (pSubtag == NULL) {
return FALSE;
}
return _isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag));
U_CFUNC UBool
ultag_isUnicodeLocaleAttributes(const char* s, int32_t len) {
return _isSepListOf(&ultag_isUnicodeLocaleAttribute, s, len);
}
U_CFUNC UBool
ultag_isUnicodeLocaleKey(const char* s, int32_t len) {
/*
* key = alphanum alpha ;
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
@ -620,9 +614,160 @@ ultag_isUnicodeLocaleKey(const char* s, int32_t len) {
return FALSE;
}
U_CFUNC UBool
_isUnicodeLocaleTypeSubtag(const char*s, int32_t len) {
/*
* alphanum{3,8}
*/
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
}
U_CFUNC UBool
ultag_isUnicodeLocaleType(const char*s, int32_t len) {
/*
* type = alphanum{3,8} (sep alphanum{3,8})* ;
*/
return _isSepListOf(&_isUnicodeLocaleTypeSubtag, s, len);
}
static UBool
_isTKey(const char* s, int32_t len)
{
/*
* tkey = alpha digit ;
*/
if (len < 0) {
len = (int32_t)uprv_strlen(s);
}
if (len == 2 && ISALPHA(*s) && ISNUMERIC(*(s + 1))) {
return TRUE;
}
return FALSE;
}
static UBool
_isTValue(const char* s, int32_t len)
{
/*
* tvalue = (sep alphanum{3,8})+ ;
*/
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
}
static UBool
_isTransformedExtensionSubtag(int32_t& state, const char* s, int32_t len)
{
const int32_t kStart = 0; // Start, wait for unicode_language_subtag, tkey or end
const int32_t kGotLanguage = 1; // Got unicode_language_subtag, wait for unicode_script_subtag,
// unicode_region_subtag, unicode_variant_subtag, tkey or end
const int32_t kGotScript = 2; // Got unicode_script_subtag, wait for unicode_region_subtag,
// unicode_variant_subtag, tkey, or end
const int32_t kGotRegion = 3; // Got unicode_region_subtag, wait for unicode_variant_subtag,
// tkey, or end.
const int32_t kGotVariant = 4; // Got unicode_variant_subtag, wait for unicode_variant_subtag
// tkey or end.
const int32_t kGotTKey = -1; // Got tkey, wait for tvalue. ERROR if stop here.
const int32_t kGotTValue = 6; // Got tvalue, wait for tkey, tvalue or end
switch (state) {
case kStart:
if (ultag_isLanguageSubtag(s, len)) {
state = kGotLanguage;
return TRUE;
}
if (_isTKey(s, len)) {
state = kGotTKey;
return TRUE;
}
return FALSE;
case kGotLanguage:
if (ultag_isScriptSubtag(s, len)) {
state = kGotScript;
return TRUE;
}
U_FALLTHROUGH;
case kGotScript:
if (ultag_isRegionSubtag(s, len)) {
state = kGotRegion;
return TRUE;
}
U_FALLTHROUGH;
case kGotRegion:
U_FALLTHROUGH;
case kGotVariant:
if (_isVariantSubtag(s, len)) {
state = kGotVariant;
return TRUE;
}
if (_isTKey(s, len)) {
state = kGotTKey;
return TRUE;
}
return FALSE;
case kGotTKey:
if (_isTValue(s, len)) {
state = kGotTValue;
return TRUE;
}
return FALSE;
case kGotTValue:
if (_isTKey(s, len)) {
state = kGotTKey;
return TRUE;
}
if (_isTValue(s, len)) {
return TRUE;
}
return FALSE;
}
return FALSE;
}
static UBool
_isUnicodeExtensionSubtag(int32_t& state, const char* s, int32_t len)
{
const int32_t kStart = 0; // Start, wait for a key or attribute or end
const int32_t kGotKey = 1; // Got a key, wait for type or key or end
const int32_t kGotType = 2; // Got a type, wait for key or end
switch (state) {
case kStart:
if (ultag_isUnicodeLocaleKey(s, len)) {
state = kGotKey;
return TRUE;
}
if (ultag_isUnicodeLocaleAttribute(s, len)) {
return TRUE;
}
return FALSE;
case kGotKey:
if (ultag_isUnicodeLocaleKey(s, len)) {
return TRUE;
}
if (_isUnicodeLocaleTypeSubtag(s, len)) {
state = kGotType;
return TRUE;
}
return FALSE;
case kGotType:
if (ultag_isUnicodeLocaleKey(s, len)) {
state = kGotKey;
return TRUE;
}
if (_isUnicodeLocaleTypeSubtag(s, len)) {
return TRUE;
}
return FALSE;
}
return FALSE;
}
static UBool
_isStatefulSepListOf(UBool (*test)(int32_t&, const char*, int32_t), const char* s, int32_t len)
{
int32_t state = 0;
const char* p;
const char* start = s;
int32_t subtagLen = 0;
if (len < 0) {
@ -631,22 +776,34 @@ ultag_isUnicodeLocaleType(const char*s, int32_t len) {
for (p = s; len > 0; p++, len--) {
if (*p == SEP) {
if (subtagLen < 3) {
if (!test(state, start, subtagLen)) {
return FALSE;
}
subtagLen = 0;
} else if (ISALPHA(*p) || ISNUMERIC(*p)) {
subtagLen++;
if (subtagLen > 8) {
return FALSE;
}
start = p + 1;
} else {
return FALSE;
subtagLen++;
}
}
return (subtagLen >= 3);
if (test(state, start, subtagLen) && state >= 0) {
return TRUE;
}
return FALSE;
}
U_CFUNC UBool
ultag_isTransformedExtensionSubtags(const char* s, int32_t len)
{
return _isStatefulSepListOf(&_isTransformedExtensionSubtag, s, len);
}
U_CFUNC UBool
ultag_isUnicodeExtensionSubtags(const char* s, int32_t len) {
return _isStatefulSepListOf(&_isUnicodeExtensionSubtag, s, len);
}
/*
* -------------------------------------------------
*
@ -856,7 +1013,7 @@ _appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
if (len == 0) {
sink.Append(LANG_UND, LANG_UND_LEN);
} else if (!_isLanguageSubtag(buf, len)) {
} else if (!ultag_isLanguageSubtag(buf, len)) {
/* invalid language code */
if (strict) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
@ -900,7 +1057,7 @@ _appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool stri
}
if (len > 0) {
if (!_isScriptSubtag(buf, len)) {
if (!ultag_isScriptSubtag(buf, len)) {
/* invalid script code */
if (strict) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
@ -932,7 +1089,7 @@ _appendRegionToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool stri
}
if (len > 0) {
if (!_isRegionSubtag(buf, len)) {
if (!ultag_isRegionSubtag(buf, len)) {
/* invalid region code */
if (strict) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
@ -1252,7 +1409,7 @@ _appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
}
} else {
if (*key == PRIVATEUSE) {
if (!_isPrivateuseValueSubtags(buf.data(), len)) {
if (!ultag_isPrivateuseValueSubtags(buf.data(), len)) {
if (strict) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
break;
@ -1260,7 +1417,7 @@ _appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
continue;
}
} else {
if (!_isExtensionSingleton(key, keylen) || !_isExtensionSubtags(buf.data(), len)) {
if (!_isExtensionSingleton(key, keylen) || !ultag_isExtensionSubtags(buf.data(), len)) {
if (strict) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
break;
@ -1997,7 +2154,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
subtagLen = (int32_t)(pSep - pSubtag);
if (next & LANG) {
if (_isLanguageSubtag(pSubtag, subtagLen)) {
if (ultag_isLanguageSubtag(pSubtag, subtagLen)) {
*pSep = 0; /* terminate */
// TODO: move deprecated language code handling here.
t->language = T_CString_toLowerCase(pSubtag);
@ -2024,7 +2181,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
}
}
if (next & SCRT) {
if (_isScriptSubtag(pSubtag, subtagLen)) {
if (ultag_isScriptSubtag(pSubtag, subtagLen)) {
char *p = pSubtag;
*pSep = 0;
@ -2044,7 +2201,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
}
}
if (next & REGN) {
if (_isRegionSubtag(pSubtag, subtagLen)) {
if (ultag_isRegionSubtag(pSubtag, subtagLen)) {
*pSep = 0;
// TODO: move deprecated region code handling here.
t->region = T_CString_toUpperCase(pSubtag);
@ -2535,7 +2692,7 @@ ulocimp_toLanguageTag(const char* localeID,
buf[1] = SEP;
len = uloc_getKeywordValue(localeID, key, &buf[2], sizeof(buf) - 2, &tmpStatus);
if (U_SUCCESS(tmpStatus)) {
if (_isPrivateuseValueSubtags(&buf[2], len)) {
if (ultag_isPrivateuseValueSubtags(&buf[2], len)) {
/* return private use only tag */
sink.Append(buf, len + 2);
done = TRUE;

View file

@ -148,6 +148,32 @@ ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion,
U_CAPI const char * U_EXPORT2
locale_getKeywordsStart(const char *localeID);
U_CFUNC UBool
ultag_isExtensionSubtags(const char* s, int32_t len);
U_CFUNC UBool
ultag_isLanguageSubtag(const char* s, int32_t len);
U_CFUNC UBool
ultag_isPrivateuseValueSubtags(const char* s, int32_t len);
U_CFUNC UBool
ultag_isRegionSubtag(const char* s, int32_t len);
U_CFUNC UBool
ultag_isScriptSubtag(const char* s, int32_t len);
U_CFUNC UBool
ultag_isTransformedExtensionSubtags(const char* s, int32_t len);
U_CFUNC UBool
ultag_isUnicodeExtensionSubtags(const char* s, int32_t len);
U_CFUNC UBool
ultag_isUnicodeLocaleAttribute(const char* s, int32_t len);
U_CFUNC UBool
ultag_isUnicodeLocaleAttributes(const char* s, int32_t len);
U_CFUNC UBool
ultag_isUnicodeLocaleKey(const char* s, int32_t len);
@ -155,6 +181,9 @@ ultag_isUnicodeLocaleKey(const char* s, int32_t len);
U_CFUNC UBool
ultag_isUnicodeLocaleType(const char* s, int32_t len);
U_CFUNC UBool
ultag_isVariantSubtags(const char* s, int32_t len);
U_CFUNC const char*
ulocimp_toBcpKey(const char* key);

View file

@ -0,0 +1,288 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
#ifndef __LOCALEBUILDER_H__
#define __LOCALEBUILDER_H__
#include "unicode/locid.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
#include "unicode/utypes.h"
/**
* \file
* \brief C++ API: Builder API for Locale
*/
U_NAMESPACE_BEGIN
class CharString;
#ifndef U_HIDE_DRAFT_API
/**
* <code>LocaleBuilder</code> is used to build instances of <code>Locale</code>
* from values configured by the setters. Unlike the <code>Locale</code>
* constructors, the <code>LocaleBuilder</code> checks if a value configured by a
* setter satisfies the syntax requirements defined by the <code>Locale</code>
* class. A <code>Locale</code> object created by a <code>LocaleBuilder</code> is
* well-formed and can be transformed to a well-formed IETF BCP 47 language tag
* without losing information.
*
* <p>The following example shows how to create a <code>Locale</code> object
* with the <code>LocaleBuilder</code>.
* <blockquote>
* <pre>
* UErrorCode status = U_ZERO_ERROR;
* Locale aLocale = LocaleBuilder()
* .setLanguage("sr")
* .setScript("Latn")
* .setRegion("RS")
* .build(status);
* if (U_SUCCESS(status)) {
* // ...
* }
* </pre>
* </blockquote>
*
* <p>LocaleBuilders can be reused; <code>clear()</code> resets all
* fields to their default values.
*
* <p>LocaleBuilder tracks errors in an internal UErrorCode. For all setters,
* except setLanguageTag and setLocale, LocaleBuilder will return immediately
* if the internal UErrorCode is in error state.
* To reset internal state and error code, call clear method.
* The setLanguageTag and setLocale method will first clear the internal
* UErrorCode, then track the error of the validation of the input parameter
* into the internal UErrorCode.
*
* @draft ICU 64
*/
class U_COMMON_API LocaleBuilder : public UObject {
public:
/**
* Constructs an empty LocaleBuilder. The default value of all
* fields, extensions, and private use information is the
* empty string.
*
* @draft ICU 64
*/
LocaleBuilder();
virtual ~LocaleBuilder();
/**
* Resets the <code>LocaleBuilder</code> to match the provided
* <code>locale</code>. Existing state is discarded.
*
* <p>All fields of the locale must be well-formed.
* <p>This method clears the internal UErrorCode.
*
* @param locale the locale
* @return This builder.
*
* @draft ICU 64
*/
LocaleBuilder& setLocale(const Locale& locale);
/**
* Resets the LocaleBuilder to match the provided
* [Unicode Locale Identifier](http://www.unicode.org/reports/tr35/tr35.html#unicode_locale_id) .
* Discards the existing state. the empty string cause the builder to be
* reset, like {@link #clear}. Grandfathered tags are converted to their
* canonical form before being processed. Otherwise, the <code>language
* tag</code> must be well-formed, or else the build() method will later
* report an U_ILLEGAL_ARGUMENT_ERROR.
*
* <p>This method clears the internal UErrorCode.
*
* @param tag the language tag, defined as
* [unicode_locale_id](http://www.unicode.org/reports/tr35/tr35.html#unicode_locale_id).
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setLanguageTag(StringPiece tag);
/**
* Sets the language. If <code>language</code> is the empty string, the
* language in this <code>LocaleBuilder</code> is removed. Otherwise, the
* <code>language</code> must be well-formed, or else the build() method will
* later report an U_ILLEGAL_ARGUMENT_ERROR.
*
* <p>The syntax of language value is defined as
* [unicode_language_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_language_subtag).
*
* @param language the language
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setLanguage(StringPiece language);
/**
* Sets the script. If <code>script</code> is the empty string, the script in
* this <code>LocaleBuilder</code> is removed.
* Otherwise, the <code>script</code> must be well-formed, or else the build()
* method will later report an U_ILLEGAL_ARGUMENT_ERROR.
*
* <p>The script value is a four-letter script code as
* [unicode_script_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_script_subtag)
* defined by ISO 15924
*
* @param script the script
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setScript(StringPiece script);
/**
* Sets the region. If region is the empty string, the region in this
* <code>LocaleBuilder</code> is removed. Otherwise, the <code>region</code>
* must be well-formed, or else the build() method will later report an
* U_ILLEGAL_ARGUMENT_ERROR.
*
* <p>The region value is defined by
* [unicode_region_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_region_subtag)
* as a two-letter ISO 3166 code or a three-digit UN M.49 area code.
*
* <p>The region value in the <code>Locale</code> created by the
* <code>LocaleBuilder</code> is always normalized to upper case.
*
* @param region the region
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setRegion(StringPiece region);
/**
* Sets the variant. If variant is the empty string, the variant in this
* <code>LocaleBuilder</code> is removed. Otherwise, the <code>variant</code>
* must be well-formed, or else the build() method will later report an
* U_ILLEGAL_ARGUMENT_ERROR.
*
* <p><b>Note:</b> This method checks if <code>variant</code>
* satisfies the
* [unicode_variant_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_variant_subtag)
* syntax requirements, and normalizes the value to lowercase letters. However,
* the <code>Locale</code> class does not impose any syntactic
* restriction on variant. To set an ill-formed variant, use a Locale constructor.
* If there are multiple unicode_variant_subtag, the caller must concatenate
* them with '-' as separator (ex: "foobar-fibar").
*
* @param variant the variant
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setVariant(StringPiece variant);
/**
* Sets the extension for the given key. If the value is the empty string,
* the extension is removed. Otherwise, the <code>key</code> and
* <code>value</code> must be well-formed, or else the build() method will
* later report an U_ILLEGAL_ARGUMENT_ERROR.
*
* <p><b>Note:</b> The key ('u') is used for the Unicode locale extension.
* Setting a value for this key replaces any existing Unicode locale key/type
* pairs with those defined in the extension.
*
* <p><b>Note:</b> The key ('x') is used for the private use code. To be
* well-formed, the value for this key needs only to have subtags of one to
* eight alphanumeric characters, not two to eight as in the general case.
*
* @param key the extension key
* @param value the extension value
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setExtension(char key, StringPiece value);
/**
* Sets the Unicode locale keyword type for the given key. If the type
* StringPiece is constructed with a nullptr, the keyword is removed.
* If the type is the empty string, the keyword is set without type subtags.
* Otherwise, the key and type must be well-formed, or else the build()
* method will later report an U_ILLEGAL_ARGUMENT_ERROR.
*
* <p>Keys and types are converted to lower case.
*
* <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
* replaces all Unicode locale keywords with those defined in the
* extension.
*
* @param key the Unicode locale key
* @param type the Unicode locale type
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& setUnicodeLocaleKeyword(
StringPiece key, StringPiece type);
/**
* Adds a unicode locale attribute, if not already present, otherwise
* has no effect. The attribute must not be empty string and must be
* well-formed or U_ILLEGAL_ARGUMENT_ERROR will be set to status
* during the build() call.
*
* @param attribute the attribute
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& addUnicodeLocaleAttribute(StringPiece attribute);
/**
* Removes a unicode locale attribute, if present, otherwise has no
* effect. The attribute must not be empty string and must be well-formed
* or U_ILLEGAL_ARGUMENT_ERROR will be set to status during the build() call.
*
* <p>Attribute comparison for removal is case-insensitive.
*
* @param attribute the attribute
* @return This builder.
* @draft ICU 64
*/
LocaleBuilder& removeUnicodeLocaleAttribute(StringPiece attribute);
/**
* Resets the builder to its initial, empty state.
* <p>This method clears the internal UErrorCode.
*
* @return this builder
* @draft ICU 64
*/
LocaleBuilder& clear();
/**
* Resets the extensions to their initial, empty state.
* Language, script, region and variant are unchanged.
*
* @return this builder
* @draft ICU 64
*/
LocaleBuilder& clearExtensions();
/**
* Returns an instance of <code>Locale</code> created from the fields set
* on this builder.
* If any set methods or during the build() call require memory allocation
* but fail U_MEMORY_ALLOCATION_ERROR will be set to status.
* If any of the fields set by the setters are not well-formed, the status
* will be set to U_ILLEGAL_ARGUMENT_ERROR. The state of the builder will
* not change after the build() call and the caller is free to keep using
* the same builder to build more locales.
*
* @return a new Locale
* @draft ICU 64
*/
Locale build(UErrorCode& status);
private:
UErrorCode status_;
char language_[9];
char script_[5];
char region_[4];
CharString *variant_; // Pointer not object so we need not #include internal charstr.h.
icu::Locale *extensions_; // Pointer not object. Storage for all other fields.
};
#endif // U_HIDE_DRAFT_API
U_NAMESPACE_END
#endif // __LOCALEBUILDER_H__

View file

@ -1109,6 +1109,16 @@
#define ulocimp_toLegacyType U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyType)
#define ultag_isUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleKey)
#define ultag_isUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleType)
#define ultag_isExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isExtensionSubtags)
#define ultag_isLanguageSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isLanguageSubtag)
#define ultag_isPrivateuseValueSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isPrivateuseValueSubtags)
#define ultag_isRegionSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isRegionSubtag)
#define ultag_isScriptSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isScriptSubtag)
#define ultag_isTransformedExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isTransformedExtensionSubtags)
#define ultag_isUnicodeExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeExtensionSubtags)
#define ultag_isUnicodeLocaleAttribute U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttribute)
#define ultag_isUnicodeLocaleAttributes U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttributes)
#define ultag_isVariantSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isVariantSubtags)
#define umsg_applyPattern U_ICU_ENTRY_POINT_RENAME(umsg_applyPattern)
#define umsg_autoQuoteApostrophe U_ICU_ENTRY_POINT_RENAME(umsg_autoQuoteApostrophe)
#define umsg_clone U_ICU_ENTRY_POINT_RENAME(umsg_clone)

View file

@ -188,6 +188,7 @@ library: common
uinit utypes errorcode
icuplug
platform
localebuilder
group: pluralmap
# TODO: Move to i18n library, ticket #11926.
@ -643,6 +644,11 @@ group: resourcebundle
uscript_props propname
bytesinkutil
group: localebuilder
localebuilder.o
deps
resourcebundle
group: udata
udata.o ucmndata.o udatamem.o
umapfile.o

View file

@ -44,7 +44,7 @@ caltztst.o canittst.o citrtest.o colldata.o convtest.o currcoll.o collationtest.
fldset.o dadrfmt.o dadrcal.o dcfmapts.o decoll.o dtfmapts.o dtfmrgts.o dtfmtrtts.o dtfmttst.o \
dtptngts.o encoll.o escoll.o ficoll.o frcoll.o g7coll.o intltest.o \
itercoll.o itformat.o itmajor.o itutil.o jacoll.o lcukocol.o \
loctest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \
loctest.o localebuildertest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \
numfmtst.o numrgts.o plurults.o plurfmts.o pptest.o regcoll.o restest.o restsnew.o \
sdtfmtts.o svccoll.o tchcfmt.o selfmts.o \
tfsmalls.o tmsgfmt.o trcoll.o tscoll.o tsdate.o tsdcfmsy.o tsdtfmsy.o \

View file

@ -364,6 +364,7 @@
<ClCompile Include="bidiconf.cpp" />
<ClCompile Include="listformattertest.cpp" />
<ClCompile Include="formattedvaluetest.cpp" />
<ClCompile Include="localebuildertest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="colldata.h" />
@ -494,8 +495,9 @@
<ClInclude Include="convtest.h" />
<ClInclude Include="csdetest.h" />
<ClInclude Include="listformattertest.h" />
<ClInclude Include="localebuildertest.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -540,6 +540,8 @@
</ClCompile>
<ClCompile Include="formattedvaluetest.cpp">
<Filter>formatting</Filter>
<ClCompile Include="localebuildertest.cpp">
<Filter>locales &amp; resources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
@ -927,5 +929,8 @@
<ClInclude Include="erarulestest.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="localebuildertest.h">
<Filter>locales &amp; resources</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>

View file

@ -19,6 +19,7 @@
#include "itutil.h"
#include "strtest.h"
#include "loctest.h"
#include "localebuildertest.h"
#include "citrtest.h"
#include "ustrtest.h"
#include "ucdtest.h"
@ -149,6 +150,7 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
}
#endif
break;
CASE(25, LocaleBuilderTest);
default: name = ""; break; //needed to end loop
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "intltest.h"
#include "unicode/localebuilder.h"
/**
* Tests for the LocaleBuilder class
**/
class LocaleBuilderTest: public IntlTest {
public:
LocaleBuilderTest();
virtual ~LocaleBuilderTest();
void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL );
void TestAddRemoveUnicodeLocaleAttribute(void);
void TestAddRemoveUnicodeLocaleAttributeWellFormed(void);
void TestAddUnicodeLocaleAttributeIllFormed(void);
void TestLocaleBuilder(void);
void TestLocaleBuilderBasic(void);
void TestPosixCases(void);
void TestSetExtensionOthers(void);
void TestSetExtensionPU(void);
void TestSetExtensionT(void);
void TestSetExtensionU(void);
void TestSetExtensionValidateOthersIllFormed(void);
void TestSetExtensionValidateOthersWellFormed(void);
void TestSetExtensionValidatePUIllFormed(void);
void TestSetExtensionValidatePUWellFormed(void);
void TestSetExtensionValidateTIllFormed(void);
void TestSetExtensionValidateTWellFormed(void);
void TestSetExtensionValidateUIllFormed(void);
void TestSetExtensionValidateUWellFormed(void);
void TestSetLanguageIllFormed(void);
void TestSetLanguageWellFormed(void);
void TestSetLocale(void);
void TestSetRegionIllFormed(void);
void TestSetRegionWellFormed(void);
void TestSetScriptIllFormed(void);
void TestSetScriptWellFormed(void);
void TestSetUnicodeLocaleKeywordIllFormedKey(void);
void TestSetUnicodeLocaleKeywordIllFormedValue(void);
void TestSetUnicodeLocaleKeywordWellFormed(void);
void TestSetVariantIllFormed(void);
void TestSetVariantWellFormed(void);
private:
void Verify(LocaleBuilder& bld, const char* expected, const char* msg);
};