+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2010-2016, International Business Machines Corporation and
#include "unicode/locdspnm.h"
#include "unicode/simpleformatter.h"
+#include "unicode/ucasemap.h"
#include "unicode/ures.h"
#include "unicode/udisplaycontext.h"
#include "unicode/brkiter.h"
}
va_start(args, buflen);
- while ((str = va_arg(args, char *))) {
+ while ((str = va_arg(args, char *)) != 0) {
char c;
- while (p != e && (c = *str++)) {
+ while (p != e && (c = *str++) != 0) {
*p++ = c;
}
}
*p = 0;
va_end(args);
- return p - buffer;
+ return static_cast<int32_t>(p - buffer);
}
U_NAMESPACE_BEGIN
: path(NULL), locale(Locale::getRoot())
{
if (path) {
- int32_t len = uprv_strlen(path);
+ int32_t len = static_cast<int32_t>(uprv_strlen(path));
this->path = (const char*) uprv_malloc(len + 1);
if (this->path) {
uprv_strcpy((char *)this->path, path);
SimpleFormatter format;
SimpleFormatter keyTypeFormat;
UDisplayContext capitalizationContext;
+#if !UCONFIG_NO_BREAK_ITERATION
BreakIterator* capitalizationBrkIter;
- static UMutex capitalizationBrkIterLock;
+#else
+ UObject* capitalizationBrkIter;
+#endif
UnicodeString formatOpenParen;
UnicodeString formatReplaceOpenParen;
UnicodeString formatCloseParen;
UnicodeString formatReplaceCloseParen;
+ UnicodeString formatParenCloseOpen;
UDisplayContext nameLength;
// Constants for capitalization context usage types.
UnicodeString& keyValueDisplayName(const char* key, const char* value,
UnicodeString& result, UBool skipAdjust) const;
void initialize(void);
-};
-UMutex LocaleDisplayNamesImpl::capitalizationBrkIterLock = U_MUTEX_INITIALIZER;
+ struct CapitalizationContextSink;
+};
LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale,
UDialectHandling dialectHandling)
initialize();
}
+struct LocaleDisplayNamesImpl::CapitalizationContextSink : public ResourceSink {
+ UBool hasCapitalizationUsage;
+ LocaleDisplayNamesImpl& parent;
+
+ CapitalizationContextSink(LocaleDisplayNamesImpl& _parent)
+ : hasCapitalizationUsage(FALSE), parent(_parent) {}
+ virtual ~CapitalizationContextSink();
+
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+ UErrorCode &errorCode) {
+ ResourceTable contexts = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int i = 0; contexts.getKeyAndValue(i, key, value); ++i) {
+
+ CapContextUsage usageEnum;
+ if (uprv_strcmp(key, "key") == 0) {
+ usageEnum = kCapContextUsageKey;
+ } else if (uprv_strcmp(key, "keyValue") == 0) {
+ usageEnum = kCapContextUsageKeyValue;
+ } else if (uprv_strcmp(key, "languages") == 0) {
+ usageEnum = kCapContextUsageLanguage;
+ } else if (uprv_strcmp(key, "script") == 0) {
+ usageEnum = kCapContextUsageScript;
+ } else if (uprv_strcmp(key, "territory") == 0) {
+ usageEnum = kCapContextUsageTerritory;
+ } else if (uprv_strcmp(key, "variant") == 0) {
+ usageEnum = kCapContextUsageVariant;
+ } else {
+ continue;
+ }
+
+ int32_t len = 0;
+ const int32_t* intVector = value.getIntVector(len, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ if (len < 2) { continue; }
+
+ int32_t titlecaseInt = (parent.capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU) ? intVector[0] : intVector[1];
+ if (titlecaseInt == 0) { continue; }
+
+ parent.fCapitalization[usageEnum] = TRUE;
+ hasCapitalizationUsage = TRUE;
+ }
+ }
+};
+
+// Virtual destructors must be defined out of line.
+LocaleDisplayNamesImpl::CapitalizationContextSink::~CapitalizationContextSink() {}
+
void
LocaleDisplayNamesImpl::initialize(void) {
LocaleDisplayNamesImpl *nonConstThis = (LocaleDisplayNamesImpl *)this;
formatReplaceOpenParen.setTo((UChar)0xFF3B); // fullwidth [
formatCloseParen.setTo((UChar)0xFF09); // fullwidth )
formatReplaceCloseParen.setTo((UChar)0xFF3D); // fullwidth ]
+ formatParenCloseOpen.setTo(u")(", 2); // fullwidth FF09 FF08 redundant parens
+
} else {
formatOpenParen.setTo((UChar)0x0028); // (
formatReplaceOpenParen.setTo((UChar)0x005B); // [
formatCloseParen.setTo((UChar)0x0029); // )
formatReplaceCloseParen.setTo((UChar)0x005D); // ]
+ formatParenCloseOpen.setTo(u") (", 3); // halfwidth redundant parens
}
UnicodeString ktPattern;
uprv_memset(fCapitalization, 0, sizeof(fCapitalization));
#if !UCONFIG_NO_BREAK_ITERATION
- // The following is basically copied from DateFormatSymbols::initializeData
- typedef struct {
- const char * usageName;
- LocaleDisplayNamesImpl::CapContextUsage usageEnum;
- } ContextUsageNameToEnum;
- const ContextUsageNameToEnum contextUsageTypeMap[] = {
- // Entries must be sorted by usageTypeName; entry with NULL name terminates list.
- { "key", kCapContextUsageKey },
- { "keyValue", kCapContextUsageKeyValue },
- { "languages", kCapContextUsageLanguage },
- { "script", kCapContextUsageScript },
- { "territory", kCapContextUsageTerritory },
- { "variant", kCapContextUsageVariant },
- { NULL, (CapContextUsage)0 },
- };
// Only get the context data if we need it! This is a const object so we know now...
// Also check whether we will need a break iterator (depends on the data)
UBool needBrkIter = FALSE;
if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE) {
- int32_t len = 0;
- UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
- if (U_SUCCESS(status)) {
- UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, "contextTransforms", NULL, &status);
- if (U_SUCCESS(status)) {
- UResourceBundle *contextTransformUsage;
- while ( (contextTransformUsage = ures_getNextResource(contextTransforms, NULL, &status)) != NULL ) {
- const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status);
- if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
- const char* usageKey = ures_getKey(contextTransformUsage);
- if (usageKey != NULL) {
- const ContextUsageNameToEnum * typeMapPtr = contextUsageTypeMap;
- int32_t compResult = 0;
- // linear search; list is short and we cannot be sure that bsearch is available
- while ( typeMapPtr->usageName != NULL && (compResult = uprv_strcmp(usageKey, typeMapPtr->usageName)) > 0 ) {
- ++typeMapPtr;
- }
- if (typeMapPtr->usageName != NULL && compResult == 0) {
- int32_t titlecaseInt = (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU)? intVector[0]: intVector[1];
- if (titlecaseInt != 0) {
- fCapitalization[typeMapPtr->usageEnum] = TRUE;;
- needBrkIter = TRUE;
- }
- }
- }
- }
- status = U_ZERO_ERROR;
- ures_close(contextTransformUsage);
- }
- ures_close(contextTransforms);
- }
- ures_close(localeBundle);
+ LocalUResourceBundlePointer resource(ures_open(NULL, locale.getName(), &status));
+ if (U_FAILURE(status)) { return; }
+ CapitalizationContextSink sink(*this);
+ ures_getAllItemsWithFallback(resource.getAlias(), "contextTransforms", sink, status);
+ if (status == U_MISSING_RESOURCE_ERROR) {
+ // Silently ignore. Not every locale has contextTransforms.
+ status = U_ZERO_ERROR;
+ } else if (U_FAILURE(status)) {
+ return;
}
+ needBrkIter = sink.hasCapitalizationUsage;
}
// Get a sentence break iterator if we will need it
if (needBrkIter || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
}
LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() {
+#if !UCONFIG_NO_BREAK_ITERATION
delete capitalizationBrkIter;
- }
+#endif
+}
const Locale&
LocaleDisplayNamesImpl::getLocale() const {
if ( result.length() > 0 && u_islower(result.char32At(0)) && capitalizationBrkIter!= NULL &&
( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || fCapitalization[usage] ) ) {
// note fCapitalization[usage] won't be set unless capitalizationContext is UI_LIST_OR_MENU or STANDALONE
- Mutex lock(&capitalizationBrkIterLock);
+ static UMutex *capitalizationBrkIterLock = STATIC_NEW(UMutex);
+ Mutex lock(capitalizationBrkIterLock);
result.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
}
#endif
}
UnicodeString&
-LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale,
+LocaleDisplayNamesImpl::localeDisplayName(const Locale& loc,
UnicodeString& result) const {
- if (locale.isBogus()) {
+ if (loc.isBogus()) {
result.setToBogus();
return result;
}
UnicodeString resultName;
- const char* lang = locale.getLanguage();
+ const char* lang = loc.getLanguage();
if (uprv_strlen(lang) == 0) {
lang = "root";
}
- const char* script = locale.getScript();
- const char* country = locale.getCountry();
- const char* variant = locale.getVariant();
+ const char* script = loc.getScript();
+ const char* country = loc.getCountry();
+ const char* variant = loc.getVariant();
UBool hasScript = uprv_strlen(script) > 0;
UBool hasCountry = uprv_strlen(country) > 0;
UBool hasVariant = uprv_strlen(variant) > 0;
- if (dialectHandling == ULDN_DIALECT_NAMES) {
+ // For stylistic reasons, always load dialect names for `zh` and `yue`. <rdar://50750364>
+ // Also for `ks`, 'pa, 'ur'. <rdar://50687287>
+ UBool forceDialect = (uprv_strcmp(lang, "zh") == 0 || uprv_strcmp(lang, "yue") == 0 ||
+ uprv_strcmp(lang, "ks") == 0 || uprv_strcmp(lang, "pa") == 0 || uprv_strcmp(lang, "ur") == 0);
+
+ if (forceDialect || dialectHandling == ULDN_DIALECT_NAMES) {
char buffer[ULOC_FULLNAME_CAPACITY];
do { // loop construct is so we can break early out of search
if (hasScript && hasCountry) {
resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen);
resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen);
- LocalPointer<StringEnumeration> e(locale.createKeywords(status));
+ LocalPointer<StringEnumeration> e(loc.createKeywords(status));
if (e.isValid() && U_SUCCESS(status)) {
UnicodeString temp2;
char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; // sigh, no ULOC_VALUE_CAPACITY
const char* key;
while ((key = e->next((int32_t *)0, status)) != NULL) {
- locale.getKeywordValue(key, value, ULOC_KEYWORD_AND_VALUES_CAPACITY, status);
- if (U_FAILURE(status)) {
+ value[0] = 0;
+ loc.getKeywordValue(key, value, ULOC_KEYWORD_AND_VALUES_CAPACITY, status);
+ if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
return result;
}
keyDisplayName(key, temp, TRUE);
if (!resultRemainder.isEmpty()) {
format.format(resultName, resultRemainder, result.remove(), status);
+ int32_t indexCloseOpen = result.indexOf(formatParenCloseOpen);
+ if (indexCloseOpen >= 0) { // replace redundant close-open parens with separator <rdar://problem/50687287>
+ UnicodeString tail(result, indexCloseOpen + formatParenCloseOpen.length()); // section after close-open
+ result.retainBetween(0, indexCloseOpen); // section before close-open
+ appendWithSep(result, tail); // combine using separator
+ }
return adjustForUsageAndContext(kCapContextUsageLanguage, result);
}