]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/locdspnm.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / common / locdspnm.cpp
index 3dadf55b927ec21490258cfbe4139cbaf8ec0b2e..d271bebd204f5076868505401266dfaa9841f950 100644 (file)
@@ -1,3 +1,5 @@
+// © 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
@@ -11,6 +13,7 @@
 
 #include "unicode/locdspnm.h"
 #include "unicode/simpleformatter.h"
+#include "unicode/ucasemap.h"
 #include "unicode/ures.h"
 #include "unicode/udisplaycontext.h"
 #include "unicode/brkiter.h"
@@ -42,16 +45,16 @@ static int32_t ncat(char *buffer, uint32_t buflen, ...) {
   }
 
   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
@@ -95,7 +98,7 @@ ICUDataTable::ICUDataTable(const char* path, const Locale& locale)
     : 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);
@@ -278,12 +281,16 @@ class LocaleDisplayNamesImpl : public LocaleDisplayNames {
     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.
@@ -343,9 +350,9 @@ private:
     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)
@@ -391,6 +398,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;
@@ -417,11 +472,14 @@ LocaleDisplayNamesImpl::initialize(void) {
         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;
@@ -433,58 +491,21 @@ LocaleDisplayNamesImpl::initialize(void) {
 
     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) {
@@ -499,8 +520,10 @@ LocaleDisplayNamesImpl::initialize(void) {
 }
 
 LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() {
+#if !UCONFIG_NO_BREAK_ITERATION
     delete capitalizationBrkIter;
- }
+#endif
+}
 
 const Locale&
 LocaleDisplayNamesImpl::getLocale() const {
@@ -537,7 +560,8 @@ LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage,
     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
@@ -545,27 +569,32 @@ LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage,
 }
 
 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) {
@@ -615,14 +644,15 @@ LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale,
   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);
@@ -647,6 +677,12 @@ LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale,
 
   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);
   }