Add support for obtaining default locale in Linux and fix issue with @collation=...
authorSteve Harter <sharter@microsoft.com>
Thu, 8 Oct 2015 22:03:53 +0000 (17:03 -0500)
committerSteve Harter <sharter@microsoft.com>
Fri, 9 Oct 2015 19:17:53 +0000 (14:17 -0500)
src/corefx/System.Globalization.Native/locale.cpp
src/corefx/System.Globalization.Native/locale.hpp
src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs
src/mscorlib/corefx/System/Globalization/CultureData.cs
src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs

index e02c32d..ca4569f 100644 (file)
@@ -36,8 +36,9 @@ Locale GetLocale(const UChar* localeName, bool canonize)
 
        if (localeName != NULL)
        {
-               int32_t len = u_strlen(localeName);
-               u_UCharsToChars(localeName, localeNameTemp, len + 1);
+               // use UnicodeString.extract instead of u_UCharsToChars; u_UCharsToChars considers '@' a variant and stops
+               UnicodeString str(localeName, -1, ULOC_FULLNAME_CAPACITY);
+               str.extract(0, str.length(), localeNameTemp);
        }
 
        Locale loc;
@@ -64,9 +65,10 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng
        return U_ZERO_ERROR;
 }
 
-void FixupLocaleName(UChar* value, int32_t valueLength)
+int FixupLocaleName(UChar* value, int32_t valueLength)
 {
-       for (int i = 0; i < valueLength; i++)
+       int i = 0;
+       for (; i < valueLength; i++)
        {
                if (value[i] == (UChar)'\0')
                {
@@ -77,8 +79,9 @@ void FixupLocaleName(UChar* value, int32_t valueLength)
                        value[i] = (UChar)'-';
                }
        }
-}
 
+       return i;
+}
 
 extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t valueLength)
 {
@@ -100,3 +103,34 @@ extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t
 
        return UErrorCodeToBool(status);
 }
+
+extern "C" int32_t GetDefaultLocaleName(UChar* value, int32_t valueLength)
+{
+       Locale locale = GetLocale(NULL);
+       if (locale.isBogus())
+       {
+               // ICU should be able to get default locale
+               return UErrorCodeToBool(U_INTERNAL_PROGRAM_ERROR);
+       }
+
+       UErrorCode status = u_charsToUChars_safe(locale.getBaseName(), value, valueLength);
+       if (U_SUCCESS(status))
+       {
+               int localeNameLen = FixupLocaleName(value, valueLength);
+
+               // if collation is present, return that to managed side
+               char collationValueTemp[ULOC_KEYWORDS_CAPACITY];
+               if (locale.getKeywordValue("collation", collationValueTemp, ULOC_KEYWORDS_CAPACITY, status) > 0)
+               {
+                       // copy the collation; managed uses a "_" to represent collation (not "@collation=")
+                       status = u_charsToUChars_safe("_", &value[localeNameLen], valueLength - localeNameLen);
+                       if (U_SUCCESS(status))
+                       {
+                               status = u_charsToUChars_safe(collationValueTemp, &value[localeNameLen + 1], valueLength - localeNameLen - 1);
+                       }
+               }
+       }
+
+       return UErrorCodeToBool(status);
+}
+
index 894ea90..0c100da 100644 (file)
@@ -43,6 +43,7 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng
 Function:
 FixupLocaleName
 
-Replace underscores with hyphens to interop with existing .NET code
+Replace underscores with hyphens to interop with existing .NET code.
+Returns the length of the string.
 */
-void FixupLocaleName(UChar* value, int32_t valueLength);
+int FixupLocaleName(UChar* value, int32_t valueLength);
index e8dfe73..3c291e5 100644 (file)
@@ -19,6 +19,10 @@ internal static partial class Interop
 
         [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)]
         [return: MarshalAs(UnmanagedType.Bool)]
+        internal unsafe static extern bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength);
+
+        [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)]
+        [return: MarshalAs(UnmanagedType.Bool)]
         internal unsafe static extern bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength);
 
         [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)]
index 336db74..f53d7ce 100644 (file)
@@ -13,9 +13,6 @@ namespace System.Globalization
 {
     internal partial class CultureData
     {
-        // Win32 constants
-        const string LOCALE_NAME_SYSTEM_DEFAULT = @"!x-sys-default-locale";
-        
         // ICU constants
         const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value
         const int ICU_ULOC_FULLNAME_CAPACITY = 157;           // max size of locale name
@@ -30,47 +27,35 @@ namespace System.Globalization
             Contract.Assert(this.sRealName != null);
 
             string alternateSortName = string.Empty;
-            string realNameBuffer = null;
-            int index;
+            string realNameBuffer = this.sRealName;
 
-            bool useSystemDefault = (this.sRealName == LOCALE_NAME_SYSTEM_DEFAULT);
-            if (!useSystemDefault) //ICU uses null to obtain the default (system) locale
+            // Basic validation
+            if (realNameBuffer.Contains("@"))
             {
-                realNameBuffer = this.sRealName;
-
-                // Basic validation
-                if (realNameBuffer.Contains("@"))
-                {
-                    return false; // don't allow ICU variants to come in directly
-                }
+                return false; // don't allow ICU variants to come in directly
+            }
 
-                // Replace _ (alternate sort) with @collation= for ICU
-                index = realNameBuffer.IndexOf('_');
-                if (index > 0)
+            // Replace _ (alternate sort) with @collation= for ICU
+            int index = realNameBuffer.IndexOf('_');
+            if (index > 0)
+            {
+                if (index >= (realNameBuffer.Length - 1) // must have characters after _
+                    || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed
                 {
-                    if (index >= (realNameBuffer.Length - 1) // must have characters after _
-                        || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed
-                    {
-                        return false; // fail
-                    }
-                    alternateSortName = realNameBuffer.Substring(index + 1);
-                    realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName;
+                    return false; // fail
                 }
+                alternateSortName = realNameBuffer.Substring(index + 1);
+                realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName;
             }
 
             // Get the locale name from ICU
-            StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
-            if (!Interop.GlobalizationInterop.GetLocaleName(realNameBuffer, sb, sb.Capacity))
+            if (!GetLocaleName(realNameBuffer, out this.sWindowsName))
             {
-                StringBuilderCache.Release(sb);
                 return false; // fail
             }
 
-            // Success - use the locale name returned which may be different than realNameBuffer (casing)
-            this.sWindowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
-
             // Replace the ICU collation keyword with an _
-            index = realNameBuffer.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
+            index = this.sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
             if (index >= 0)
             {
                 this.sName = this.sWindowsName.Substring(0, index) + "_" + alternateSortName;
@@ -79,21 +64,13 @@ namespace System.Globalization
             {
                 this.sName = this.sWindowsName;
             }
-
             this.sRealName = this.sName;
             this.sSpecificCulture = this.sRealName; // we don't attempt to find a non-neutral locale if a neutral is passed in (unlike win32)
 
             this.iLanguage = this.ILANGUAGE;
             if (this.iLanguage == 0)
             {
-                if (useSystemDefault)
-                {
-                    this.iLanguage = LOCALE_CUSTOM_DEFAULT;
-                }
-                else
-                {
-                    this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED;
-                }
+                this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED;
             }
 
             this.bNeutral = (this.SISO3166CTRYNAME.Length == 0);
@@ -106,10 +83,41 @@ namespace System.Globalization
                     this.sName = this.sWindowsName.Substring(0, index);
                 }
             }
+            return true;
+        }
 
+        internal static bool GetLocaleName(string localeName, out string windowsName)
+        {
+            // Get the locale name from ICU
+            StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
+            if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity))
+            {
+                StringBuilderCache.Release(sb);
+                windowsName = null;
+                return false; // fail
+            }
+
+            // Success - use the locale name returned which may be different than realNameBuffer (casing)
+            windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
+            return true;
+        }
+
+        internal static bool GetDefaultLocaleName(out string windowsName)
+        {
+            // Get the default (system) locale name from ICU
+            StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
+            if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity))
+            {
+                StringBuilderCache.Release(sb);
+                windowsName = null;
+                return false; // fail
+            }
+
+            // Success - use the locale name returned which may be different than realNameBuffer (casing)
+            windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
             return true;
         }
+
         private string GetLocaleInfo(LocaleStringData type)
         {
             Contract.Assert(this.sWindowsName != null, "[CultureData.GetLocaleInfo] Expected this.sWindowsName to be populated already");
@@ -244,7 +252,7 @@ namespace System.Globalization
 
         private static CultureInfo GetUserDefaultCulture()
         {
-            return new CultureInfo(LOCALE_NAME_SYSTEM_DEFAULT);
+            return CultureInfo.GetUserDefaultCulture();
         }
     }
 }
index 217bda6..aabe66e 100644 (file)
@@ -1679,8 +1679,8 @@ namespace System.Globalization
             {
                 // Note: Custom cultures might point at another culture's textinfo, however windows knows how
                 // to redirect it to the desired textinfo culture, so this is OK.
-                Contract.Assert(this.sWindowsName != null, "[CultureData.STEXTINFO] Expected this.sWindowsName to be populated by already");
-                return (this.sWindowsName);
+                Contract.Assert(this.sRealName != null, "[CultureData.STEXTINFO] Expected this.sRealName to be populated by already");
+                return (this.sRealName);
             }
         }
 
@@ -1689,8 +1689,8 @@ namespace System.Globalization
         {
             get
             {
-                Contract.Assert(this.sWindowsName != null, "[CultureData.SCOMPAREINFO] Expected this.sWindowsName to be populated by already");
-                return (this.sWindowsName);
+                Contract.Assert(this.sRealName != null, "[CultureData.SCOMPAREINFO] Expected this.sRealName to be populated by already");
+                return (this.sRealName);
             }
         }
 
index a6f0b30..a786d11 100644 (file)
@@ -5,22 +5,26 @@ namespace System.Globalization
 {
     public partial class CultureInfo : IFormatProvider
     {
-        /// <summary>
-        /// Gets the default user culture from WinRT, if available.
-        /// </summary>
-        /// <remarks>
-        /// This method may return null, if there is no default user culture or if WinRT isn't available.
-        /// </remarks>
         private static CultureInfo GetUserDefaultCultureCacheOverride()
         {
-            // TODO: Implement this fully.
-            return null;
+            return null; // ICU doesn't provide a user override
         }
 
-        private static CultureInfo GetUserDefaultCulture()
+        internal static CultureInfo GetUserDefaultCulture()
         {
-            // TODO: Implement this fully.
-            return CultureInfo.InvariantCulture;
+            CultureInfo cultureInfo = null;
+            string localeName;
+            if (CultureData.GetDefaultLocaleName(out localeName))
+            {
+                cultureInfo = GetCultureByName(localeName, true);
+                cultureInfo.m_isReadOnly = true;
+            }
+            else
+            {
+                cultureInfo = CultureInfo.InvariantCulture;
+            }
+
+            return cultureInfo;
         }
     }
-}
\ No newline at end of file
+}