Index: source/common/locdspnm.cpp |
diff --git a/source/i18n/locdspnm.cpp b/source/common/locdspnm.cpp |
similarity index 82% |
rename from source/i18n/locdspnm.cpp |
rename to source/common/locdspnm.cpp |
index c0934cd5dc816b6205736c4fe593138312d7d613..a17478ce6d8a611b8ad75d573af7efa74d2518d4 100644 |
--- a/source/i18n/locdspnm.cpp |
+++ b/source/common/locdspnm.cpp |
@@ -1,6 +1,8 @@ |
+// Copyright (C) 2016 and later: Unicode, Inc. and others. |
+// License & terms of use: http://www.unicode.org/copyright.html |
/* |
******************************************************************************* |
-* Copyright (C) 2010-2014, International Business Machines Corporation and |
+* Copyright (C) 2010-2016, International Business Machines Corporation and |
* others. All Rights Reserved. |
******************************************************************************* |
*/ |
@@ -10,11 +12,11 @@ |
#if !UCONFIG_NO_FORMATTING |
#include "unicode/locdspnm.h" |
-#include "unicode/msgfmt.h" |
+#include "unicode/simpleformatter.h" |
#include "unicode/ures.h" |
-#include "unicode/udisplaycontext.h" |
+#include "unicode/udisplaycontext.h" |
#include "unicode/brkiter.h" |
- |
+#include "unicode/ucurr.h" |
#include "cmemory.h" |
#include "cstring.h" |
#include "mutex.h" |
@@ -274,11 +276,15 @@ class LocaleDisplayNamesImpl : public LocaleDisplayNames { |
UDialectHandling dialectHandling; |
ICUDataTable langData; |
ICUDataTable regionData; |
- MessageFormat *separatorFormat; |
- MessageFormat *format; |
- MessageFormat *keyTypeFormat; |
+ SimpleFormatter separatorFormat; |
+ SimpleFormatter format; |
+ SimpleFormatter keyTypeFormat; |
UDisplayContext capitalizationContext; |
- BreakIterator* capitalizationBrkIter; |
+#if !UCONFIG_NO_BREAK_ITERATION |
+ BreakIterator* capitalizationBrkIter; |
+#else |
+ UObject* capitalizationBrkIter; |
+#endif |
static UMutex capitalizationBrkIterLock; |
UnicodeString formatOpenParen; |
UnicodeString formatReplaceOpenParen; |
@@ -334,7 +340,15 @@ private: |
UnicodeString& result) const; |
UnicodeString& appendWithSep(UnicodeString& buffer, const UnicodeString& src) const; |
UnicodeString& adjustForUsageAndContext(CapContextUsage usage, UnicodeString& result) const; |
+ UnicodeString& scriptDisplayName(const char* script, UnicodeString& result, UBool skipAdjust) const; |
+ UnicodeString& regionDisplayName(const char* region, UnicodeString& result, UBool skipAdjust) const; |
+ UnicodeString& variantDisplayName(const char* variant, UnicodeString& result, UBool skipAdjust) const; |
+ UnicodeString& keyDisplayName(const char* key, UnicodeString& result, UBool skipAdjust) const; |
+ UnicodeString& keyValueDisplayName(const char* key, const char* value, |
+ UnicodeString& result, UBool skipAdjust) const; |
void initialize(void); |
+ |
+ struct CapitalizationContextSink; |
}; |
UMutex LocaleDisplayNamesImpl::capitalizationBrkIterLock = U_MUTEX_INITIALIZER; |
@@ -344,9 +358,6 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, |
: dialectHandling(dialectHandling) |
, langData(U_ICUDATA_LANG, locale) |
, regionData(U_ICUDATA_REGION, locale) |
- , separatorFormat(NULL) |
- , format(NULL) |
- , keyTypeFormat(NULL) |
, capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
, capitalizationBrkIter(NULL) |
, nameLength(UDISPCTX_LENGTH_FULL) |
@@ -359,9 +370,6 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, |
: dialectHandling(ULDN_STANDARD_NAMES) |
, langData(U_ICUDATA_LANG, locale) |
, regionData(U_ICUDATA_REGION, locale) |
- , separatorFormat(NULL) |
- , format(NULL) |
- , keyTypeFormat(NULL) |
, capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
, capitalizationBrkIter(NULL) |
, nameLength(UDISPCTX_LENGTH_FULL) |
@@ -386,6 +394,54 @@ LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, |
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; |
@@ -399,14 +455,14 @@ LocaleDisplayNamesImpl::initialize(void) { |
sep = UnicodeString("{0}, {1}", -1, US_INV); |
} |
UErrorCode status = U_ZERO_ERROR; |
- separatorFormat = new MessageFormat(sep, status); |
+ separatorFormat.applyPatternMinMaxArguments(sep, 2, 2, status); |
UnicodeString pattern; |
langData.getNoFallback("localeDisplayPattern", "pattern", pattern); |
if (pattern.isBogus()) { |
pattern = UnicodeString("{0} ({1})", -1, US_INV); |
} |
- format = new MessageFormat(pattern, status); |
+ format.applyPatternMinMaxArguments(pattern, 2, 2, status); |
if (pattern.indexOf((UChar)0xFF08) >= 0) { |
formatOpenParen.setTo((UChar)0xFF08); // fullwidth ( |
formatReplaceOpenParen.setTo((UChar)0xFF3B); // fullwidth [ |
@@ -424,62 +480,25 @@ LocaleDisplayNamesImpl::initialize(void) { |
if (ktPattern.isBogus()) { |
ktPattern = UnicodeString("{0}={1}", -1, US_INV); |
} |
- keyTypeFormat = new MessageFormat(ktPattern, status); |
+ keyTypeFormat.applyPatternMinMaxArguments(ktPattern, 2, 2, status); |
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) { |
@@ -494,11 +513,10 @@ LocaleDisplayNamesImpl::initialize(void) { |
} |
LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() { |
- delete separatorFormat; |
- delete format; |
- delete keyTypeFormat; |
+#if !UCONFIG_NO_BREAK_ITERATION |
delete capitalizationBrkIter; |
- } |
+#endif |
+} |
const Locale& |
LocaleDisplayNamesImpl::getLocale() const { |
@@ -543,6 +561,10 @@ LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage, |
UnicodeString& |
LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale, |
UnicodeString& result) const { |
+ if (locale.isBogus()) { |
+ result.setToBogus(); |
+ return result; |
+ } |
UnicodeString resultName; |
const char* lang = locale.getLanguage(); |
@@ -593,45 +615,41 @@ LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale, |
UnicodeString resultRemainder; |
UnicodeString temp; |
- StringEnumeration *e = NULL; |
UErrorCode status = U_ZERO_ERROR; |
if (hasScript) { |
- resultRemainder.append(scriptDisplayName(script, temp)); |
+ resultRemainder.append(scriptDisplayName(script, temp, TRUE)); |
} |
if (hasCountry) { |
- appendWithSep(resultRemainder, regionDisplayName(country, temp)); |
+ appendWithSep(resultRemainder, regionDisplayName(country, temp, TRUE)); |
} |
if (hasVariant) { |
- appendWithSep(resultRemainder, variantDisplayName(variant, temp)); |
+ appendWithSep(resultRemainder, variantDisplayName(variant, temp, TRUE)); |
} |
resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
- e = locale.createKeywords(status); |
- if (e && U_SUCCESS(status)) { |
+ LocalPointer<StringEnumeration> e(locale.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); |
- keyDisplayName(key, temp); |
+ if (U_FAILURE(status)) { |
+ return result; |
+ } |
+ keyDisplayName(key, temp, TRUE); |
temp.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
temp.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
- keyValueDisplayName(key, value, temp2); |
+ keyValueDisplayName(key, value, temp2, TRUE); |
temp2.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
temp2.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
if (temp2 != UnicodeString(value, -1, US_INV)) { |
appendWithSep(resultRemainder, temp2); |
} else if (temp != UnicodeString(key, -1, US_INV)) { |
UnicodeString temp3; |
- Formattable data[] = { |
- temp, |
- temp2 |
- }; |
- FieldPosition fpos; |
- status = U_ZERO_ERROR; |
- keyTypeFormat->format(data, 2, temp3, fpos, status); |
+ keyTypeFormat.format(temp, temp2, temp3, status); |
appendWithSep(resultRemainder, temp3); |
} else { |
appendWithSep(resultRemainder, temp) |
@@ -639,17 +657,10 @@ LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale, |
.append(temp2); |
} |
} |
- delete e; |
} |
if (!resultRemainder.isEmpty()) { |
- Formattable data[] = { |
- resultName, |
- resultRemainder |
- }; |
- FieldPosition fpos; |
- status = U_ZERO_ERROR; |
- format->format(data, 2, result, fpos, status); |
+ format.format(resultName, resultRemainder, result.remove(), status); |
return adjustForUsageAndContext(kCapContextUsageLanguage, result); |
} |
@@ -662,17 +673,9 @@ LocaleDisplayNamesImpl::appendWithSep(UnicodeString& buffer, const UnicodeString |
if (buffer.isEmpty()) { |
buffer.setTo(src); |
} else { |
- UnicodeString combined; |
- Formattable data[] = { |
- buffer, |
- src |
- }; |
- FieldPosition fpos; |
+ const UnicodeString *values[2] = { &buffer, &src }; |
UErrorCode status = U_ZERO_ERROR; |
- separatorFormat->format(data, 2, combined, fpos, status); |
- if (U_SUCCESS(status)) { |
- buffer.setTo(combined); |
- } |
+ separatorFormat.formatAndReplace(values, 2, buffer, NULL, 0, status); |
} |
return buffer; |
} |
@@ -714,56 +717,86 @@ LocaleDisplayNamesImpl::languageDisplayName(const char* lang, |
UnicodeString& |
LocaleDisplayNamesImpl::scriptDisplayName(const char* script, |
- UnicodeString& result) const { |
+ UnicodeString& result, |
+ UBool skipAdjust) const { |
if (nameLength == UDISPCTX_LENGTH_SHORT) { |
langData.get("Scripts%short", script, result); |
if (!result.isBogus()) { |
- return adjustForUsageAndContext(kCapContextUsageScript, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); |
} |
} |
langData.get("Scripts", script, result); |
- return adjustForUsageAndContext(kCapContextUsageScript, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); |
+} |
+ |
+UnicodeString& |
+LocaleDisplayNamesImpl::scriptDisplayName(const char* script, |
+ UnicodeString& result) const { |
+ return scriptDisplayName(script, result, FALSE); |
} |
UnicodeString& |
LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode, |
UnicodeString& result) const { |
- return scriptDisplayName(uscript_getName(scriptCode), result); |
+ return scriptDisplayName(uscript_getName(scriptCode), result, FALSE); |
} |
UnicodeString& |
LocaleDisplayNamesImpl::regionDisplayName(const char* region, |
- UnicodeString& result) const { |
+ UnicodeString& result, |
+ UBool skipAdjust) const { |
if (nameLength == UDISPCTX_LENGTH_SHORT) { |
regionData.get("Countries%short", region, result); |
if (!result.isBogus()) { |
- return adjustForUsageAndContext(kCapContextUsageTerritory, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); |
} |
} |
regionData.get("Countries", region, result); |
- return adjustForUsageAndContext(kCapContextUsageTerritory, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); |
+} |
+ |
+UnicodeString& |
+LocaleDisplayNamesImpl::regionDisplayName(const char* region, |
+ UnicodeString& result) const { |
+ return regionDisplayName(region, result, FALSE); |
} |
+ |
UnicodeString& |
LocaleDisplayNamesImpl::variantDisplayName(const char* variant, |
- UnicodeString& result) const { |
+ UnicodeString& result, |
+ UBool skipAdjust) const { |
// don't have a resource for short variant names |
langData.get("Variants", variant, result); |
- return adjustForUsageAndContext(kCapContextUsageVariant, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageVariant, result); |
+} |
+ |
+UnicodeString& |
+LocaleDisplayNamesImpl::variantDisplayName(const char* variant, |
+ UnicodeString& result) const { |
+ return variantDisplayName(variant, result, FALSE); |
} |
UnicodeString& |
LocaleDisplayNamesImpl::keyDisplayName(const char* key, |
- UnicodeString& result) const { |
+ UnicodeString& result, |
+ UBool skipAdjust) const { |
// don't have a resource for short key names |
langData.get("Keys", key, result); |
- return adjustForUsageAndContext(kCapContextUsageKey, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKey, result); |
+} |
+ |
+UnicodeString& |
+LocaleDisplayNamesImpl::keyDisplayName(const char* key, |
+ UnicodeString& result) const { |
+ return keyDisplayName(key, result, FALSE); |
} |
UnicodeString& |
LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, |
const char* value, |
- UnicodeString& result) const { |
+ UnicodeString& result, |
+ UBool skipAdjust) const { |
if (uprv_strcmp(key, "currency") == 0) { |
// ICU4C does not have ICU4J CurrencyDisplayInfo equivalent for now. |
UErrorCode sts = U_ZERO_ERROR; |
@@ -778,17 +811,24 @@ LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, |
return result; |
} |
result.setTo(currencyName, len); |
- return adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
} |
if (nameLength == UDISPCTX_LENGTH_SHORT) { |
langData.get("Types%short", key, value, result); |
if (!result.isBogus()) { |
- return adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
} |
} |
langData.get("Types", key, value, result); |
- return adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
+ return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); |
+} |
+ |
+UnicodeString& |
+LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, |
+ const char* value, |
+ UnicodeString& result) const { |
+ return keyValueDisplayName(key, value, result, FALSE); |
} |
//////////////////////////////////////////////////////////////////////////////////////////////////// |
@@ -887,6 +927,10 @@ uldn_localeDisplayName(const ULocaleDisplayNames *ldn, |
} |
UnicodeString temp(result, 0, maxResultSize); |
((const LocaleDisplayNames *)ldn)->localeDisplayName(locale, temp); |
+ if (temp.isBogus()) { |
+ *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
+ return 0; |
+ } |
return temp.extract(result, maxResultSize, *pErrorCode); |
} |