diff options
Diffstat (limited to 'src/mscorlib/src/System/Globalization/CultureInfo.cs')
-rw-r--r-- | src/mscorlib/src/System/Globalization/CultureInfo.cs | 1357 |
1 files changed, 482 insertions, 875 deletions
diff --git a/src/mscorlib/src/System/Globalization/CultureInfo.cs b/src/mscorlib/src/System/Globalization/CultureInfo.cs index ba61c146f6..60938defac 100644 --- a/src/mscorlib/src/System/Globalization/CultureInfo.cs +++ b/src/mscorlib/src/System/Globalization/CultureInfo.cs @@ -26,24 +26,27 @@ // //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { - using System; - using System.Security; - using System.Threading; - using System.Collections; - using System.Runtime; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Runtime.Serialization; - using System.Runtime.Versioning; - using System.Reflection; - using Microsoft.Win32; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Resources; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Globalization +{ +#if CORECLR + using StringCultureInfoDictionary = Dictionary<string, CultureInfo>; + using StringLcidDictionary = Dictionary<int, CultureInfo>; + + using Lock = Object; +#else + using StringCultureInfoDictionary = LowLevelDictionary<string, CultureInfo>; + using StringLcidDictionary = LowLevelDictionary<int, CultureInfo>; +#endif [Serializable] - public partial class CultureInfo : ICloneable, IFormatProvider { + public partial class CultureInfo : IFormatProvider, ICloneable + { //--------------------------------------------------------------------// // Internal Information // //--------------------------------------------------------------------// @@ -53,38 +56,32 @@ namespace System.Globalization { //--------------------------------------------------------------------// // We use an RFC4646 type string to construct CultureInfo. - // This string is stored in m_name and is authoritative. - // We use the m_cultureData to get the data for our object - - // WARNING - // WARNING: All member fields declared here must also be in ndp/clr/src/vm/object.h - // WARNING: They aren't really private because object.h can access them, but other C# stuff cannot - // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureInfoBaseObject - // WARNING: must be manually structured to match the true loaded class layout - // WARNING - internal bool m_isReadOnly; - internal CompareInfo compareInfo; - internal TextInfo textInfo; + // This string is stored in _name and is authoritative. + // We use the _cultureData to get the data for our object + + private bool _isReadOnly; + private CompareInfo compareInfo; + private TextInfo textInfo; internal NumberFormatInfo numInfo; internal DateTimeFormatInfo dateTimeInfo; - internal Calendar calendar; - [OptionalField(VersionAdded = 1)] - internal int m_dataItem; // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey/Everett) - [OptionalField(VersionAdded = 1)] - internal int cultureID = 0x007f; // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey/Everett) + private Calendar calendar; // // The CultureData instance that we are going to read data from. // For supported culture, this will be the CultureData instance that read data from mscorlib assembly. // For customized culture, this will be the CultureData instance that read data from user customized culture binary file. // - [NonSerialized]internal CultureData m_cultureData; - - [NonSerialized]internal bool m_isInherited; - [NonSerialized]private CultureInfo m_consoleFallbackCulture; + [NonSerialized] + internal CultureData _cultureData; + + [NonSerialized] + internal bool _isInherited; + + [NonSerialized] + private CultureInfo _consoleFallbackCulture; // Names are confusing. Here are 3 names we have: // - // new CultureInfo() m_name m_nonSortName m_sortName + // new CultureInfo() _name _nonSortName _sortName // en-US en-US en-US en-US // de-de_phoneb de-DE_phoneb de-DE de-DE_phoneb // fj-fj (custom) fj-FJ fj-FJ en-US (if specified sort is en-US) @@ -96,17 +93,18 @@ namespace System.Globalization { // Note that the name used to be serialized for Everett; it is now serialized // because alernate sorts can have alternate names. // This has a de-DE, de-DE_phoneb or fj-FJ style name - internal string m_name; + internal string _name; // This will hold the non sorting name to be returned from CultureInfo.Name property. // This has a de-DE style name even for de-DE_phoneb type cultures - [NonSerialized]private string m_nonSortName; + [NonSerialized] + private string _nonSortName; // This will hold the sorting name to be returned from CultureInfo.SortName property. // This might be completely unrelated to the culture name if a custom culture. Ie en-US for fj-FJ. // Otherwise its the sort name, ie: de-DE or de-DE_phoneb - [NonSerialized]private string m_sortName; - + [NonSerialized] + private string _sortName; //--------------------------------------------------------------------// // @@ -117,184 +115,74 @@ namespace System.Globalization { //Get the current user default culture. This one is almost always used, so we create it by default. private static volatile CultureInfo s_userDefaultCulture; + //The culture used in the user interface. This is mostly used to load correct localized resources. + private static volatile CultureInfo s_userDefaultUICulture; // // All of the following will be created on demand. // + // WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture) + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + //The Invariant culture; private static volatile CultureInfo s_InvariantCultureInfo; - //The culture used in the user interface. This is mostly used to load correct localized resources. - private static volatile CultureInfo s_userDefaultUICulture; - - //This is the UI culture used to install the OS. - private static volatile CultureInfo s_InstalledUICultureInfo; - //These are defaults that we use if a thread has not opted into having an explicit culture private static volatile CultureInfo s_DefaultThreadCurrentUICulture; private static volatile CultureInfo s_DefaultThreadCurrentCulture; - //This is a cache of all previously created cultures. Valid keys are LCIDs or the name. We use two hashtables to track them, - // depending on how they are called. - private static volatile Hashtable s_LcidCachedCultures; - private static volatile Hashtable s_NameCachedCultures; + internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture; + internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture; -#if FEATURE_APPX - // When running under AppX, we use this to get some information about the language list - private static volatile WindowsRuntimeResourceManagerBase s_WindowsRuntimeResourceManager; + internal static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args) + { + Thread.m_CurrentCulture = args.CurrentValue; + } - [ThreadStatic] - private static bool ts_IsDoingAppXCultureInfoLookup; -#endif + internal static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args) + { + Thread.m_CurrentUICulture = args.CurrentValue; + } + + private static readonly Lock _lock = new Lock(); + private static volatile StringCultureInfoDictionary s_NameCachedCultures; + private static volatile StringLcidDictionary s_LcidCachedCultures; //The parent culture. - [NonSerialized]private CultureInfo m_parent; + [NonSerialized] + private CultureInfo _parent; // LOCALE constants of interest to us internally and privately for LCID functions // (ie: avoid using these and use names if possible) - internal const int LOCALE_NEUTRAL = 0x0000; - private const int LOCALE_USER_DEFAULT = 0x0400; - private const int LOCALE_SYSTEM_DEFAULT = 0x0800; - internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00; - internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000; - internal const int LOCALE_INVARIANT = 0x007F; - private const int LOCALE_TRADITIONAL_SPANISH = 0x040a; + internal const int LOCALE_NEUTRAL = 0x0000; + private const int LOCALE_USER_DEFAULT = 0x0400; + private const int LOCALE_SYSTEM_DEFAULT = 0x0800; + internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000; + internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00; + internal const int LOCALE_INVARIANT = 0x007F; // // The CultureData instance that reads the data provided by our CultureData class. // - //Using a field initializer rather than a static constructor so that the whole class can be lazy - //init. + // Using a field initializer rather than a static constructor so that the whole class can be lazy + // init. private static readonly bool init = Init(); private static bool Init() { - - if (s_InvariantCultureInfo == null) + if (s_InvariantCultureInfo == null) { CultureInfo temp = new CultureInfo("", false); - temp.m_isReadOnly = true; + temp._isReadOnly = true; s_InvariantCultureInfo = temp; } - // First we set it to Invariant in case someone needs it before we're done finding it. - // For example, if we throw an exception in InitUserDefaultCulture, we will still need an valid - // s_userDefaultCulture to be used in Thread.CurrentCulture. - s_userDefaultCulture = s_userDefaultUICulture = s_InvariantCultureInfo; - s_userDefaultCulture = InitUserDefaultCulture(); - s_userDefaultUICulture = InitUserDefaultUICulture(); + s_userDefaultCulture = GetUserDefaultCulture(); + s_userDefaultUICulture = GetUserDefaultUILanguage(); return true; } - static CultureInfo InitUserDefaultCulture() - { - String strDefault = GetDefaultLocaleName(LOCALE_USER_DEFAULT); - if (strDefault == null) - { - strDefault = GetDefaultLocaleName(LOCALE_SYSTEM_DEFAULT); - - if (strDefault == null) - { - // If system default doesn't work, keep using the invariant - return (CultureInfo.InvariantCulture); - } - } - CultureInfo temp = GetCultureByName(strDefault, true); - - temp.m_isReadOnly = true; - - return (temp); - } - - static CultureInfo InitUserDefaultUICulture() - { - String strDefault = GetUserDefaultUILanguage(); - - // In most of cases, UserDefaultCulture == UserDefaultUICulture, so we should use the same instance if possible. - if (strDefault == UserDefaultCulture.Name) - { - return (UserDefaultCulture); - } - - CultureInfo temp = GetCultureByName( strDefault, true); - - if (temp == null) - { - return (CultureInfo.InvariantCulture); - } - - temp.m_isReadOnly = true; - - return (temp); - } - -#if FEATURE_APPX - internal static CultureInfo GetCultureInfoForUserPreferredLanguageInAppX() - { - // If a call to GetCultureInfoForUserPreferredLanguageInAppX() generated a recursive - // call to itself, return null, since we don't want to stack overflow. For example, - // this can happen if some code in this method ends up calling CultureInfo.CurrentCulture - // (which is common on check'd build because of BCLDebug logging which calls Int32.ToString()). - // In this case, returning null will mean CultureInfo.CurrentCulture gets the default Win32 - // value, which should be fine. - if(ts_IsDoingAppXCultureInfoLookup) - { - return null; - } - - // If running within a compilation process (mscorsvw.exe, for example), it is illegal to - // load any non-mscorlib assembly for execution. Since WindowsRuntimeResourceManager lives - // in System.Runtime.WindowsRuntime, caller will need to fall back to default Win32 value, - // which should be fine because we should only ever need to access FX resources during NGEN. - // FX resources are always loaded from satellite assemblies - even in AppX processes (see the - // comments in code:System.Resources.ResourceManager.SetAppXConfiguration for more details). - if (AppDomain.IsAppXNGen) - { - return null; - } - - CultureInfo toReturn = null; - - try - { - ts_IsDoingAppXCultureInfoLookup = true; - - if(s_WindowsRuntimeResourceManager == null) - { - s_WindowsRuntimeResourceManager = ResourceManager.GetWinRTResourceManager(); - } - - toReturn = s_WindowsRuntimeResourceManager.GlobalResourceContextBestFitCultureInfo; - } - finally - { - ts_IsDoingAppXCultureInfoLookup = false; - } - - return toReturn; - } - - internal static bool SetCultureInfoForUserPreferredLanguageInAppX(CultureInfo ci) - { - // If running within a compilation process (mscorsvw.exe, for example), it is illegal to - // load any non-mscorlib assembly for execution. Since WindowsRuntimeResourceManager lives - // in System.Runtime.WindowsRuntime, caller will need to fall back to default Win32 value, - // which should be fine because we should only ever need to access FX resources during NGEN. - // FX resources are always loaded from satellite assemblies - even in AppX processes (see the - // comments in code:System.Resources.ResourceManager.SetAppXConfiguration for more details). - if (AppDomain.IsAppXNGen) - { - return false; - } - - if (s_WindowsRuntimeResourceManager == null) - { - s_WindowsRuntimeResourceManager = ResourceManager.GetWinRTResourceManager(); - } - - return s_WindowsRuntimeResourceManager.SetGlobalResourceContextDefaultCulture(ci); - } -#endif - //////////////////////////////////////////////////////////////////////// // // CultureInfo Constructors @@ -302,38 +190,33 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public CultureInfo(String name) : this(name, true) { + public CultureInfo(String name) + : this(name, true) + { } - public CultureInfo(String name, bool useUserOverride) { - if (name==null) { + public CultureInfo(String name, bool useUserOverride) + { + if (name == null) + { throw new ArgumentNullException(nameof(name), - Environment.GetResourceString("ArgumentNull_String")); + SR.ArgumentNull_String); } - Contract.EndContractBlock(); - - // Get our data providing record - this.m_cultureData = CultureData.GetCultureData(name, useUserOverride); - if (this.m_cultureData == null) { - throw new CultureNotFoundException(nameof(name), name, Environment.GetResourceString("Argument_CultureNotSupported")); - } - - this.m_name = this.m_cultureData.CultureName; - this.m_isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); + InitializeFromName(name, useUserOverride); } - -#if FEATURE_USE_LCID - public CultureInfo(int culture) : this(culture, true) { + public CultureInfo(int culture) : this(culture, true) + { } - public CultureInfo(int culture, bool useUserOverride) { + public CultureInfo(int culture, bool useUserOverride) + { // We don't check for other invalid LCIDS here... - if (culture < 0) { - throw new ArgumentOutOfRangeException(nameof(culture), - Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + if (culture < 0) + { + throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum); } Contract.EndContractBlock(); @@ -351,88 +234,31 @@ namespace System.Globalization { case LOCALE_CUSTOM_UNSPECIFIED: // Can't support unknown custom cultures and we do not support neutral or // non-custom user locales. - throw new CultureNotFoundException( - nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported")); + throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); default: - // Now see if this LCID is supported in the system default CultureData table. - this.m_cultureData = CultureData.GetCultureData(culture, useUserOverride); + // Now see if this LCID is supported in the system default CultureData table. + _cultureData = CultureData.GetCultureData(culture, useUserOverride); break; } - this.m_isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); - this.m_name = this.m_cultureData.CultureName; + _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); + _name = _cultureData.CultureName; } -#endif // FEATURE_USE_LCID - -#region Serialization - // We need to store the override from the culture data record. - private bool m_useUserOverride; - [OnDeserialized] - private void OnDeserialized(StreamingContext ctx) + private void InitializeFromName(string name, bool useUserOverride) { -#if FEATURE_USE_LCID - // Whidbey+ should remember our name - // but v1 and v1.1 did not store name -- only lcid - // Whidbey did not store actual alternate sort name in m_name - // like we do in v4 so we can't use name for alternate sort - // e.g. for es-ES_tradnl: v2 puts es-ES in m_name; v4 puts es-ES_tradnl - if (m_name == null || IsAlternateSortLcid(cultureID)) - { - Debug.Assert(cultureID >=0, "[CultureInfo.OnDeserialized] cultureID >= 0"); - InitializeFromCultureId(cultureID, m_useUserOverride); - } - else - { -#endif - Debug.Assert(m_name != null, "[CultureInfo.OnDeserialized] m_name != null"); - - this.m_cultureData = CultureData.GetCultureData(m_name, m_useUserOverride); - if (this.m_cultureData == null) - throw new CultureNotFoundException( - nameof(m_name), m_name, Environment.GetResourceString("Argument_CultureNotSupported")); - -#if FEATURE_USE_LCID - } -#endif - m_isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); - } - -#if FEATURE_USE_LCID - // A locale ID is a 32 bit value which is the combination of a - // language ID, a sort ID, and a reserved area. The bits are - // allocated as follows: - // - // +------------------------+-------+--------------------------------+ - // | Reserved |Sort ID| Language ID | - // +------------------------+-------+--------------------------------+ - // 31 20 19 16 15 0 bit - private const int LOCALE_SORTID_MASK = 0x000f0000; + // Get our data providing record + _cultureData = CultureData.GetCultureData(name, useUserOverride); - static private bool IsAlternateSortLcid(int lcid) - { - if(lcid == LOCALE_TRADITIONAL_SPANISH) + if (_cultureData == null) { - return true; + throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported); } - return (lcid & LOCALE_SORTID_MASK) != 0; - } -#endif - - [OnSerializing] - private void OnSerializing(StreamingContext ctx) - { - this.m_name = this.m_cultureData.CultureName; - this.m_useUserOverride = this.m_cultureData.UseUserOverride; -#if FEATURE_USE_LCID - // for compatibility with v2 serialize cultureID - this.cultureID = this.m_cultureData.ILANGUAGE; -#endif + _name = _cultureData.CultureName; + _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); } -#endregion Serialization - // Constructor called by SQL Server's special munged culture - creates a culture with // a TextInfo and CompareInfo that come from a supplied alternate source. This object // is ALWAYS read-only. @@ -441,38 +267,46 @@ namespace System.Globalization { // the GetCultureInfo override *only*. internal CultureInfo(String cultureName, String textAndCompareCultureName) { - if (cultureName==null) { - throw new ArgumentNullException(nameof(cultureName), - Environment.GetResourceString("ArgumentNull_String")); + if (cultureName == null) + { + throw new ArgumentNullException(nameof(cultureName),SR.ArgumentNull_String); } Contract.EndContractBlock(); - this.m_cultureData = CultureData.GetCultureData(cultureName, false); - if (this.m_cultureData == null) - throw new CultureNotFoundException( - nameof(cultureName), cultureName, Environment.GetResourceString("Argument_CultureNotSupported")); + _cultureData = CultureData.GetCultureData(cultureName, false); + if (_cultureData == null) + throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported); - this.m_name = this.m_cultureData.CultureName; + _name = _cultureData.CultureName; CultureInfo altCulture = GetCultureInfo(textAndCompareCultureName); - this.compareInfo = altCulture.CompareInfo; - this.textInfo = altCulture.TextInfo; + compareInfo = altCulture.CompareInfo; + textInfo = altCulture.TextInfo; } // We do this to try to return the system UI language and the default user languages - // The callers should have a fallback if this fails (like Invariant) + // This method will fallback if this fails (like Invariant) + // + // TODO: It would appear that this is only ever called with userOveride = true + // and this method only has one caller. Can we fold it into the caller? private static CultureInfo GetCultureByName(String name, bool userOverride) - { + { + CultureInfo ci = null; // Try to get our culture try { - return userOverride ? new CultureInfo(name) : CultureInfo.GetCultureInfo(name); + ci = userOverride ? new CultureInfo(name) : CultureInfo.GetCultureInfo(name); } catch (ArgumentException) { } - return null; + if (ci == null) + { + ci = InvariantCulture; + } + + return ci; } // @@ -483,14 +317,18 @@ namespace System.Globalization { // if we can't find a bigger name. That doesn't help with things like "zh" though, so // the approach is of questionable value // - public static CultureInfo CreateSpecificCulture(String name) { + public static CultureInfo CreateSpecificCulture(String name) + { Contract.Ensures(Contract.Result<CultureInfo>() != null); CultureInfo culture; - try { + try + { culture = new CultureInfo(name); - } catch(ArgumentException) { + } + catch (ArgumentException) + { // When CultureInfo throws this exception, it may be because someone passed the form // like "az-az" because it came out of an http accept lang. We should try a little // parsing to perhaps fall back to "az" here and use *it* to create the neutral. @@ -498,269 +336,160 @@ namespace System.Globalization { int idx; culture = null; - for(idx = 0; idx < name.Length; idx++) { - if('-' == name[idx]) { - try { + for (idx = 0; idx < name.Length; idx++) + { + if ('-' == name[idx]) + { + try + { culture = new CultureInfo(name.Substring(0, idx)); break; - } catch(ArgumentException) { + } + catch (ArgumentException) + { // throw the original exception so the name in the string will be right throw; } } } - if(null == culture) { + if (culture == null) + { // nothing to save here; throw the original exception throw; } } - //In the most common case, they've given us a specific culture, so we'll just return that. - if (!(culture.IsNeutralCulture)) { + // In the most common case, they've given us a specific culture, so we'll just return that. + if (!(culture.IsNeutralCulture)) + { return culture; } - return (new CultureInfo(culture.m_cultureData.SSPECIFICCULTURE)); + return (new CultureInfo(culture._cultureData.SSPECIFICCULTURE)); } - internal static bool VerifyCultureName(String cultureName, bool throwException) + internal static bool VerifyCultureName(String cultureName, bool throwException) { - // This function is used by ResourceManager.GetResourceFileName(). + // This function is used by ResourceManager.GetResourceFileName(). // ResourceManager searches for resource using CultureInfo.Name, // so we should check against CultureInfo.Name. - for (int i=0; i<cultureName.Length; i++) { + for (int i = 0; i < cultureName.Length; i++) + { char c = cultureName[i]; - - if (Char.IsLetterOrDigit(c) || c=='-' || c=='_') { + // TODO: Names can only be RFC4646 names (ie: a-zA-Z0-9) while this allows any unicode letter/digit + if (Char.IsLetterOrDigit(c) || c == '-' || c == '_') + { continue; } - if (throwException) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidResourceCultureName", cultureName)); + if (throwException) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidResourceCultureName, cultureName)); } return false; } return true; - } - internal static bool VerifyCultureName(CultureInfo culture, bool throwException) { - Debug.Assert(culture!=null, "[CultureInfo.VerifyCultureName]culture!=null"); - + internal static bool VerifyCultureName(CultureInfo culture, bool throwException) + { //If we have an instance of one of our CultureInfos, the user can't have changed the //name and we know that all names are valid in files. - if (!culture.m_isInherited) { + if (!culture._isInherited) + { return true; } return VerifyCultureName(culture.Name, throwException); - } - //////////////////////////////////////////////////////////////////////// - // - // CurrentCulture - // - // This instance provides methods based on the current user settings. - // These settings are volatile and may change over the lifetime of the - // thread. - // - //////////////////////////////////////////////////////////////////////// - + // We need to store the override from the culture data record. + private bool _useUserOverride; - public static CultureInfo CurrentCulture + [OnSerializing] + private void OnSerializing(StreamingContext ctx) { - get { - Contract.Ensures(Contract.Result<CultureInfo>() != null); - - // In the case of CoreCLR, Thread.m_CurrentCulture and - // Thread.m_CurrentUICulture are thread static so as not to let - // CultureInfo objects leak across AppDomain boundaries. The - // fact that these fields are thread static introduces overhead - // in accessing them (through Thread.CurrentCulture). There is - // also overhead in accessing Thread.CurrentThread. In this - // case, we can avoid the overhead of Thread.CurrentThread - // because these fields are thread static, and so do not - // require a Thread instance to be accessed. -#if FEATURE_APPX - if(AppDomain.IsAppXModel()) { - CultureInfo culture = GetCultureInfoForUserPreferredLanguageInAppX(); - if (culture != null) - return culture; - } -#endif - return Thread.m_CurrentCulture ?? - s_DefaultThreadCurrentCulture ?? - s_userDefaultCulture ?? - UserDefaultCulture; - } - - set { -#if FEATURE_APPX - if (value == null) { - throw new ArgumentNullException(nameof(value)); - } - - if (AppDomain.IsAppXModel()) { - if (SetCultureInfoForUserPreferredLanguageInAppX(value)) { - // successfully set the culture, otherwise fallback to legacy path - return; - } - } -#endif - Thread.CurrentThread.CurrentCulture = value; - } + _name = _cultureData.CultureName; + _useUserOverride = _cultureData.UseUserOverride; } - // - // This is the equivalence of the Win32 GetUserDefaultLCID() - // - internal static CultureInfo UserDefaultCulture { - get + [OnDeserialized] + private void OnDeserialized(StreamingContext ctx) + { + Debug.Assert(_name != null, "[CultureInfo.OnDeserialized] _name != null"); + InitializeFromName(_name, _useUserOverride); + } + + internal static CultureInfo GetCurrentUICultureNoAppX() + { + CultureInfo ci = GetUserDefaultCultureCacheOverride(); + if (ci != null) { - Contract.Ensures(Contract.Result<CultureInfo>() != null); - - CultureInfo temp = s_userDefaultCulture; - if (temp == null) - { - // - // setting the s_userDefaultCulture with invariant culture before intializing it is a protection - // against recursion problem just in case if somebody called CurrentCulture from the CultureInfo - // creation path. the recursion can happen if the current user culture is a replaced custom culture. - // - - s_userDefaultCulture = CultureInfo.InvariantCulture; - temp = InitUserDefaultCulture(); - s_userDefaultCulture = temp; - } - return (temp); + return ci; } - } - // - // This is the equivalence of the Win32 GetUserDefaultUILanguage() - // - internal static CultureInfo UserDefaultUICulture { - get { - Contract.Ensures(Contract.Result<CultureInfo>() != null); - - CultureInfo temp = s_userDefaultUICulture; - if (temp == null) - { - // - // setting the s_userDefaultCulture with invariant culture before intializing it is a protection - // against recursion problem just in case if somebody called CurrentUICulture from the CultureInfo - // creation path. the recursion can happen if the current user culture is a replaced custom culture. - // - - s_userDefaultUICulture = CultureInfo.InvariantCulture; - - temp = InitUserDefaultUICulture(); - s_userDefaultUICulture = temp; - } - return (temp); + if (Thread.m_CurrentUICulture != null) + { + return Thread.m_CurrentUICulture; } - } + ci = s_DefaultThreadCurrentUICulture; + if (ci != null) + { + return ci; + } - public static CultureInfo CurrentUICulture { - get { - Contract.Ensures(Contract.Result<CultureInfo>() != null); - - // In the case of CoreCLR, Thread.m_CurrentCulture and - // Thread.m_CurrentUICulture are thread static so as not to let - // CultureInfo objects leak across AppDomain boundaries. The - // fact that these fields are thread static introduces overhead - // in accessing them (through Thread.CurrentCulture). There is - // also overhead in accessing Thread.CurrentThread. In this - // case, we can avoid the overhead of Thread.CurrentThread - // because these fields are thread static, and so do not - // require a Thread instance to be accessed. -#if FEATURE_APPX - if(AppDomain.IsAppXModel()) { - CultureInfo culture = GetCultureInfoForUserPreferredLanguageInAppX(); - if (culture != null) - return culture; - } -#endif - return Thread.m_CurrentUICulture ?? - s_DefaultThreadCurrentUICulture ?? - s_userDefaultUICulture ?? - UserDefaultUICulture; - } - - set { -#if FEATURE_APPX - if (value == null) { - throw new ArgumentNullException(nameof(value)); - } - - if (AppDomain.IsAppXModel()) { - if (SetCultureInfoForUserPreferredLanguageInAppX(value)) { - // successfully set the culture, otherwise fallback to legacy path - return; - } - } -#endif - Thread.CurrentThread.CurrentUICulture = value; + // if s_userDefaultUICulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static + // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize + if (s_userDefaultUICulture == null) + { + Init(); } - } + Debug.Assert(s_userDefaultUICulture != null); + return s_userDefaultUICulture; + } - // - // This is the equivalence of the Win32 GetSystemDefaultUILanguage() - // - public static CultureInfo InstalledUICulture { - get { + public static CultureInfo InstalledUICulture + { + get + { Contract.Ensures(Contract.Result<CultureInfo>() != null); - - CultureInfo temp = s_InstalledUICultureInfo; - if (temp == null) { - String strDefault = GetSystemDefaultUILanguage(); - temp = GetCultureByName(strDefault, true); - - if (temp == null) - { - temp = InvariantCulture; - } - - temp.m_isReadOnly = true; - s_InstalledUICultureInfo = temp; + if (s_userDefaultCulture == null) + { + Init(); } - return (temp); + Debug.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null"); + return s_userDefaultCulture; } } - public static CultureInfo DefaultThreadCurrentCulture { - get { - return s_DefaultThreadCurrentCulture; - } - - set { - - // If you add pre-conditions to this method, check to see if you also need to + public static CultureInfo DefaultThreadCurrentCulture + { + get { return s_DefaultThreadCurrentCulture; } + set + { + // If you add pre-conditions to this method, check to see if you also need to // add them to Thread.CurrentCulture.set. s_DefaultThreadCurrentCulture = value; } } - public static CultureInfo DefaultThreadCurrentUICulture { - get { - return s_DefaultThreadCurrentUICulture; - } - - set { - + public static CultureInfo DefaultThreadCurrentUICulture + { + get { return s_DefaultThreadCurrentUICulture; } + set + { //If they're trying to use a Culture with a name that we can't use in resource lookup, //don't even let them set it on the thread. - // If you add more pre-conditions to this method, check to see if you also need to + // If you add more pre-conditions to this method, check to see if you also need to // add them to Thread.CurrentUICulture.set. - if (value != null) - { + if (value != null) + { CultureInfo.VerifyCultureName(value, true); } @@ -782,10 +511,10 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public static CultureInfo InvariantCulture { - [Pure] - get { - Contract.Ensures(Contract.Result<CultureInfo>() != null); + public static CultureInfo InvariantCulture + { + get + { return (s_InvariantCultureInfo); } } @@ -803,21 +532,19 @@ namespace System.Globalization { { get { - Contract.Ensures(Contract.Result<CultureInfo>() != null); - CultureInfo culture = null; - - if (null == m_parent) + if (null == _parent) { try { - string parentName = this.m_cultureData.SPARENT; + string parentName = _cultureData.SPARENT; + if (String.IsNullOrEmpty(parentName)) { - culture = InvariantCulture; + _parent = InvariantCulture; } else { - culture = new CultureInfo(parentName, this.m_cultureData.UseUserOverride); + _parent = new CultureInfo(parentName, _cultureData.UseUserOverride); } } catch (ArgumentException) @@ -825,58 +552,35 @@ namespace System.Globalization { // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant // We can't allow ourselves to fail. In case of custom cultures the parent of the // current custom culture isn't installed. - culture = InvariantCulture; + _parent = InvariantCulture; } - - Interlocked.CompareExchange<CultureInfo>(ref m_parent, culture, null); } - return m_parent; + return _parent; } } - //////////////////////////////////////////////////////////////////////// - // - // LCID - // - // Returns a properly formed culture identifier for the current - // culture info. - // - //////////////////////////////////////////////////////////////////////// - -#if FEATURE_USE_LCID - public virtual int LCID { - get { - return (this.m_cultureData.ILANGUAGE); + public virtual int LCID + { + get + { + return (this._cultureData.ILANGUAGE); } } -#endif - //////////////////////////////////////////////////////////////////////// - // - // BaseInputLanguage - // - // Essentially an LCID, though one that may be different than LCID in the case - // of a customized culture (LCID == LOCALE_CUSTOM_UNSPECIFIED). - // - //////////////////////////////////////////////////////////////////////// -#if FEATURE_USE_LCID public virtual int KeyboardLayoutId { get { - int keyId = this.m_cultureData.IINPUTLANGUAGEHANDLE; - - // Not a customized culture, return the default Keyboard layout ID, which is the same as the language ID. - return (keyId); + return _cultureData.IINPUTLANGUAGEHANDLE; } } -#endif - public static CultureInfo[] GetCultures(CultureTypes types) { + public static CultureInfo[] GetCultures(CultureTypes types) + { Contract.Ensures(Contract.Result<CultureInfo[]>() != null); // internally we treat UserCustomCultures as Supplementals but v2 // treats as Supplementals and Replacements - if((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture) + if ((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture) { types |= CultureTypes.ReplacementCultures; } @@ -891,18 +595,20 @@ namespace System.Globalization { // "en-US" This version does NOT include sort information in the name. // //////////////////////////////////////////////////////////////////////// - public virtual String Name { - get { - Contract.Ensures(Contract.Result<String>() != null); - + public virtual String Name + { + get + { // We return non sorting name here. - if (this.m_nonSortName == null) { - this.m_nonSortName = this.m_cultureData.SNAME; - if (this.m_nonSortName == null) { - this.m_nonSortName = String.Empty; + if (_nonSortName == null) + { + _nonSortName = _cultureData.SNAME; + if (_nonSortName == null) + { + _nonSortName = String.Empty; } } - return this.m_nonSortName; + return _nonSortName; } } @@ -911,20 +617,20 @@ namespace System.Globalization { { get { - if (this.m_sortName == null) + if (_sortName == null) { - this.m_sortName = this.m_cultureData.SCOMPAREINFO; + _sortName = _cultureData.SCOMPAREINFO; } - return this.m_sortName; + return _sortName; } } - public String IetfLanguageTag + public string IetfLanguageTag { get { - Contract.Ensures(Contract.Result<String>() != null); + Contract.Ensures(Contract.Result<string>() != null); // special case the compatibility cultures switch (this.Name) @@ -953,9 +659,9 @@ namespace System.Globalization { get { Contract.Ensures(Contract.Result<String>() != null); - Debug.Assert(m_name != null, "[CultureInfo.DisplayName]Always expect m_name to be set"); + Debug.Assert(_name != null, "[CultureInfo.DisplayName] Always expect _name to be set"); - return m_cultureData.SLOCALIZEDDISPLAYNAME; + return _cultureData.SLOCALIZEDDISPLAYNAME; } } @@ -968,10 +674,12 @@ namespace System.Globalization { // (United States)" will be returned. // //////////////////////////////////////////////////////////////////////// - public virtual String NativeName { - get { + public virtual String NativeName + { + get + { Contract.Ensures(Contract.Result<String>() != null); - return (this.m_cultureData.SNATIVEDISPLAYNAME); + return (_cultureData.SNATIVEDISPLAYNAME); } } @@ -984,26 +692,32 @@ namespace System.Globalization { // (United States)" will be returned. // //////////////////////////////////////////////////////////////////////// - public virtual String EnglishName { - get { + public virtual String EnglishName + { + get + { Contract.Ensures(Contract.Result<String>() != null); - return (this.m_cultureData.SENGDISPLAYNAME); + return (_cultureData.SENGDISPLAYNAME); } } - + // ie: en - public virtual String TwoLetterISOLanguageName { - get { + public virtual String TwoLetterISOLanguageName + { + get + { Contract.Ensures(Contract.Result<String>() != null); - return (this.m_cultureData.SISO639LANGNAME); + return (_cultureData.SISO639LANGNAME); } } // ie: eng - public virtual String ThreeLetterISOLanguageName { - get { + public virtual String ThreeLetterISOLanguageName + { + get + { Contract.Ensures(Contract.Result<String>() != null); - return (this.m_cultureData.SISO639LANGNAME2); + return _cultureData.SISO639LANGNAME2; } } @@ -1015,10 +729,12 @@ namespace System.Globalization { // The ISO names are much preferred // //////////////////////////////////////////////////////////////////////// - public virtual String ThreeLetterWindowsLanguageName { - get { + public virtual String ThreeLetterWindowsLanguageName + { + get + { Contract.Ensures(Contract.Result<String>() != null); - return (this.m_cultureData.SABBREVLANGNAME); + return _cultureData.SABBREVLANGNAME; } } @@ -1033,16 +749,14 @@ namespace System.Globalization { { get { - Contract.Ensures(Contract.Result<CompareInfo>() != null); - if (this.compareInfo == null) { // Since CompareInfo's don't have any overrideable properties, get the CompareInfo from // the Non-Overridden CultureInfo so that we only create one CompareInfo per culture - CompareInfo temp = UseUserOverride - ? GetCultureInfo(this.m_name).CompareInfo + CompareInfo temp = UseUserOverride + ? GetCultureInfo(this._name).CompareInfo : new CompareInfo(this); - if (CompatibilitySwitches.IsCompatibilityBehaviorDefined) + if (OkayToCacheClassWithCompatibilityBehavior) { this.compareInfo = temp; } @@ -1055,6 +769,14 @@ namespace System.Globalization { } } + private static bool OkayToCacheClassWithCompatibilityBehavior + { + get + { + return true; + } + } + //////////////////////////////////////////////////////////////////////// // // TextInfo @@ -1062,19 +784,17 @@ namespace System.Globalization { // Gets the TextInfo for this culture. // //////////////////////////////////////////////////////////////////////// - - - public virtual TextInfo TextInfo { - get { - Contract.Ensures(Contract.Result<TextInfo>() != null); - - if (textInfo==null) + public virtual TextInfo TextInfo + { + get + { + if (textInfo == null) { // Make a new textInfo - TextInfo tempTextInfo = new TextInfo(this.m_cultureData); - tempTextInfo.SetReadOnlyState(m_isReadOnly); + TextInfo tempTextInfo = new TextInfo(_cultureData); + tempTextInfo.SetReadOnlyState(_isReadOnly); - if (CompatibilitySwitches.IsCompatibilityBehaviorDefined) + if (OkayToCacheClassWithCompatibilityBehavior) { textInfo = tempTextInfo; } @@ -1144,26 +864,24 @@ namespace System.Globalization { public override String ToString() { - Contract.Ensures(Contract.Result<String>() != null); - - Debug.Assert(m_name != null, "[CultureInfo.ToString]Always expect m_name to be set"); - return m_name; + return _name; } - public virtual Object GetFormat(Type formatType) { - if (formatType == typeof(NumberFormatInfo)) { + public virtual Object GetFormat(Type formatType) + { + if (formatType == typeof(NumberFormatInfo)) return (NumberFormat); - } - if (formatType == typeof(DateTimeFormatInfo)) { + if (formatType == typeof(DateTimeFormatInfo)) return (DateTimeFormat); - } return (null); } - public virtual bool IsNeutralCulture { - get { - return this.m_cultureData.IsNeutralCulture; + public virtual bool IsNeutralCulture + { + get + { + return _cultureData.IsNeutralCulture; } } @@ -1173,43 +891,43 @@ namespace System.Globalization { { CultureTypes types = 0; - if (m_cultureData.IsNeutralCulture) + if (_cultureData.IsNeutralCulture) types |= CultureTypes.NeutralCultures; - else + else types |= CultureTypes.SpecificCultures; - types |= m_cultureData.IsWin32Installed ? CultureTypes.InstalledWin32Cultures : 0; + types |= _cultureData.IsWin32Installed ? CultureTypes.InstalledWin32Cultures : 0; -// Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete + // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete #pragma warning disable 618 - types |= m_cultureData.IsFramework ? CultureTypes.FrameworkCultures : 0; -#pragma warning restore 618 + types |= _cultureData.IsFramework ? CultureTypes.FrameworkCultures : 0; - types |= m_cultureData.IsSupplementalCustomCulture ? CultureTypes.UserCustomCulture : 0; - types |= m_cultureData.IsReplacementCulture ? CultureTypes.ReplacementCultures | CultureTypes.UserCustomCulture : 0; +#pragma warning restore 618 + types |= _cultureData.IsSupplementalCustomCulture ? CultureTypes.UserCustomCulture : 0; + types |= _cultureData.IsReplacementCulture ? CultureTypes.ReplacementCultures | CultureTypes.UserCustomCulture : 0; return types; } } - public virtual NumberFormatInfo NumberFormat { - get + public virtual NumberFormatInfo NumberFormat + { + get { - Contract.Ensures(Contract.Result<NumberFormatInfo>() != null); - - if (numInfo == null) { - NumberFormatInfo temp = new NumberFormatInfo(this.m_cultureData); - temp.isReadOnly = m_isReadOnly; + if (numInfo == null) + { + NumberFormatInfo temp = new NumberFormatInfo(_cultureData); + temp.isReadOnly = _isReadOnly; Interlocked.CompareExchange(ref numInfo, temp, null); } return (numInfo); } - set { - if (value == null) { - throw new ArgumentNullException(nameof(value), - Environment.GetResourceString("ArgumentNull_Obj")); + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj); } - Contract.EndContractBlock(); VerifyWritable(); numInfo = value; } @@ -1223,44 +941,40 @@ namespace System.Globalization { // the CultureID. // //////////////////////////////////////////////////////////////////////// - - - public virtual DateTimeFormatInfo DateTimeFormat { - get { - Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null); - - if (dateTimeInfo == null) { + public virtual DateTimeFormatInfo DateTimeFormat + { + get + { + if (dateTimeInfo == null) + { // Change the calendar of DTFI to the specified calendar of this CultureInfo. - DateTimeFormatInfo temp = new DateTimeFormatInfo( - this.m_cultureData, this.Calendar); - temp.m_isReadOnly = m_isReadOnly; + DateTimeFormatInfo temp = new DateTimeFormatInfo(_cultureData, this.Calendar); + temp._isReadOnly = _isReadOnly; Interlocked.CompareExchange(ref dateTimeInfo, temp, null); } return (dateTimeInfo); } - set { - if (value == null) { - throw new ArgumentNullException(nameof(value), - Environment.GetResourceString("ArgumentNull_Obj")); + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj); } - Contract.EndContractBlock(); VerifyWritable(); dateTimeInfo = value; } } - public void ClearCachedData() { - s_userDefaultUICulture = null; + public void ClearCachedData() + { s_userDefaultCulture = null; RegionInfo.s_currentRegionInfo = null; -#pragma warning disable CS0618 + #pragma warning disable 0618 // disable the obsolete warning TimeZone.ResetTimeZone(); -#pragma warning restore CS0618 + #pragma warning restore 0618 TimeZoneInfo.ClearCachedData(); - - // Delete the cached cultures. s_LcidCachedCultures = null; s_NameCachedCultures = null; @@ -1275,8 +989,10 @@ namespace System.Globalization { ** Shouldn't throw exception since the calType value is from our data table or from Win32 registry. ** If we are in trouble (like getting a weird value from Win32 registry), just return the GregorianCalendar. ============================================================================*/ - internal static Calendar GetCalendarInstance(int calType) { - if (calType==Calendar.CAL_GREGORIAN) { + internal static Calendar GetCalendarInstance(CalendarId calType) + { + if (calType == CalendarId.GREGORIAN) + { return (new GregorianCalendar()); } return GetCalendarInstanceRare(calType); @@ -1284,45 +1000,38 @@ namespace System.Globalization { //This function exists as a shortcut to prevent us from loading all of the non-gregorian //calendars unless they're required. - internal static Calendar GetCalendarInstanceRare(int calType) { - Debug.Assert(calType!=Calendar.CAL_GREGORIAN, "calType!=Calendar.CAL_GREGORIAN"); - - switch (calType) { - case Calendar.CAL_GREGORIAN_US: // Gregorian (U.S.) calendar - case Calendar.CAL_GREGORIAN_ME_FRENCH: // Gregorian Middle East French calendar - case Calendar.CAL_GREGORIAN_ARABIC: // Gregorian Arabic calendar - case Calendar.CAL_GREGORIAN_XLIT_ENGLISH: // Gregorian Transliterated English calendar - case Calendar.CAL_GREGORIAN_XLIT_FRENCH: // Gregorian Transliterated French calendar + internal static Calendar GetCalendarInstanceRare(CalendarId calType) + { + Debug.Assert(calType != CalendarId.GREGORIAN, "calType!=CalendarId.GREGORIAN"); + + switch (calType) + { + case CalendarId.GREGORIAN_US: // Gregorian (U.S.) calendar + case CalendarId.GREGORIAN_ME_FRENCH: // Gregorian Middle East French calendar + case CalendarId.GREGORIAN_ARABIC: // Gregorian Arabic calendar + case CalendarId.GREGORIAN_XLIT_ENGLISH: // Gregorian Transliterated English calendar + case CalendarId.GREGORIAN_XLIT_FRENCH: // Gregorian Transliterated French calendar return (new GregorianCalendar((GregorianCalendarTypes)calType)); - case Calendar.CAL_TAIWAN: // Taiwan Era calendar + case CalendarId.TAIWAN: // Taiwan Era calendar return (new TaiwanCalendar()); - case Calendar.CAL_JAPAN: // Japanese Emperor Era calendar + case CalendarId.JAPAN: // Japanese Emperor Era calendar return (new JapaneseCalendar()); - case Calendar.CAL_KOREA: // Korean Tangun Era calendar + case CalendarId.KOREA: // Korean Tangun Era calendar return (new KoreanCalendar()); - case Calendar.CAL_THAI: // Thai calendar + case CalendarId.THAI: // Thai calendar return (new ThaiBuddhistCalendar()); - case Calendar.CAL_HIJRI: // Hijri (Arabic Lunar) calendar + case CalendarId.HIJRI: // Hijri (Arabic Lunar) calendar return (new HijriCalendar()); - case Calendar.CAL_HEBREW: // Hebrew (Lunar) calendar + case CalendarId.HEBREW: // Hebrew (Lunar) calendar return (new HebrewCalendar()); - case Calendar.CAL_UMALQURA: + case CalendarId.UMALQURA: return (new UmAlQuraCalendar()); - case Calendar.CAL_PERSIAN: + case CalendarId.PERSIAN: return (new PersianCalendar()); - case Calendar.CAL_CHINESELUNISOLAR: - return (new ChineseLunisolarCalendar()); - case Calendar.CAL_JAPANESELUNISOLAR: - return (new JapaneseLunisolarCalendar()); - case Calendar.CAL_KOREANLUNISOLAR: - return (new KoreanLunisolarCalendar()); - case Calendar.CAL_TAIWANLUNISOLAR: - return (new TaiwanLunisolarCalendar()); } return (new GregorianCalendar()); } - /*=================================Calendar========================== **Action: Return/set the default calendar used by this culture. ** This value can be overridden by regional option if this is a current culture. @@ -1331,19 +1040,19 @@ namespace System.Globalization { **Exceptions: ** ArgumentNull_Obj if the set value is null. ============================================================================*/ - - - public virtual Calendar Calendar { - get { - Contract.Ensures(Contract.Result<Calendar>() != null); - if (calendar == null) { - Debug.Assert(this.m_cultureData.CalendarIds.Length > 0, "this.m_cultureData.CalendarIds.Length > 0"); + public virtual Calendar Calendar + { + get + { + if (calendar == null) + { + Debug.Assert(_cultureData.CalendarIds.Length > 0, "_cultureData.CalendarIds.Length > 0"); // Get the default calendar for this culture. Note that the value can be // from registry if this is a user default culture. - Calendar newObj = this.m_cultureData.DefaultCalendar; + Calendar newObj = _cultureData.DefaultCalendar; - System.Threading.Thread.MemoryBarrier(); - newObj.SetReadOnlyState(m_isReadOnly); + System.Threading.Interlocked.MemoryBarrier(); + newObj.SetReadOnlyState(_isReadOnly); calendar = newObj; } return (calendar); @@ -1358,26 +1067,30 @@ namespace System.Globalization { ============================================================================*/ - public virtual Calendar[] OptionalCalendars { - get { + public virtual Calendar[] OptionalCalendars + { + get + { Contract.Ensures(Contract.Result<Calendar[]>() != null); // // This property always returns a new copy of the calendar array. // - int[] calID = this.m_cultureData.CalendarIds; - Calendar [] cals = new Calendar[calID.Length]; - for (int i = 0; i < cals.Length; i++) { + CalendarId[] calID = _cultureData.CalendarIds; + Calendar[] cals = new Calendar[calID.Length]; + for (int i = 0; i < cals.Length; i++) + { cals[i] = GetCalendarInstance(calID[i]); } return (cals); } } - - public bool UseUserOverride { - get { - return (this.m_cultureData.UseUserOverride); + public bool UseUserOverride + { + get + { + return _cultureData.UseUserOverride; } } @@ -1385,26 +1098,24 @@ namespace System.Globalization { { Contract.Ensures(Contract.Result<CultureInfo>() != null); - CultureInfo temp = m_consoleFallbackCulture; + CultureInfo temp = _consoleFallbackCulture; if (temp == null) { - temp = CreateSpecificCulture(this.m_cultureData.SCONSOLEFALLBACKNAME); - temp.m_isReadOnly = true; - m_consoleFallbackCulture = temp; + temp = CreateSpecificCulture(_cultureData.SCONSOLEFALLBACKNAME); + _isReadOnly = true; + _consoleFallbackCulture = temp; } return (temp); } public virtual Object Clone() { - Contract.Ensures(Contract.Result<Object>() != null); - CultureInfo ci = (CultureInfo)MemberwiseClone(); - ci.m_isReadOnly = false; + ci._isReadOnly = false; //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless //they've already been allocated. If this is a derived type, we'll take a more generic codepath. - if (!m_isInherited) + if (!_isInherited) { if (this.dateTimeInfo != null) { @@ -1414,36 +1125,37 @@ namespace System.Globalization { { ci.numInfo = (NumberFormatInfo)this.numInfo.Clone(); } - } else { ci.DateTimeFormat = (DateTimeFormatInfo)this.DateTimeFormat.Clone(); - ci.NumberFormat = (NumberFormatInfo)this.NumberFormat.Clone(); + ci.NumberFormat = (NumberFormatInfo)this.NumberFormat.Clone(); } if (textInfo != null) { - ci.textInfo = (TextInfo) textInfo.Clone(); + ci.textInfo = (TextInfo)textInfo.Clone(); } if (calendar != null) { - ci.calendar = (Calendar) calendar.Clone(); + ci.calendar = (Calendar)calendar.Clone(); } return (ci); } - - public static CultureInfo ReadOnly(CultureInfo ci) { - if (ci == null) { + public static CultureInfo ReadOnly(CultureInfo ci) + { + if (ci == null) + { throw new ArgumentNullException(nameof(ci)); } Contract.Ensures(Contract.Result<CultureInfo>() != null); Contract.EndContractBlock(); - if (ci.IsReadOnly) { + if (ci.IsReadOnly) + { return (ci); } CultureInfo newInfo = (CultureInfo)(ci.MemberwiseClone()); @@ -1452,20 +1164,24 @@ namespace System.Globalization { { //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless //they've already been allocated. If this is a derived type, we'll take a more generic codepath. - if (!ci.m_isInherited) { - if (ci.dateTimeInfo != null) { + if (!ci._isInherited) + { + if (ci.dateTimeInfo != null) + { newInfo.dateTimeInfo = DateTimeFormatInfo.ReadOnly(ci.dateTimeInfo); } - if (ci.numInfo != null) { + if (ci.numInfo != null) + { newInfo.numInfo = NumberFormatInfo.ReadOnly(ci.numInfo); } - - } else { + } + else + { newInfo.DateTimeFormat = DateTimeFormatInfo.ReadOnly(ci.DateTimeFormat); newInfo.NumberFormat = NumberFormatInfo.ReadOnly(ci.NumberFormat); } } - + if (ci.textInfo != null) { newInfo.textInfo = TextInfo.ReadOnly(ci.textInfo); @@ -1478,23 +1194,26 @@ namespace System.Globalization { // Don't set the read-only flag too early. // We should set the read-only flag here. Otherwise, info.DateTimeFormat will not be able to set. - newInfo.m_isReadOnly = true; + newInfo._isReadOnly = true; return (newInfo); } - public bool IsReadOnly { - get { - return (m_isReadOnly); + public bool IsReadOnly + { + get + { + return (_isReadOnly); } } - private void VerifyWritable() { - if (m_isReadOnly) { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly")); + private void VerifyWritable() + { + if (_isReadOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); } - Contract.EndContractBlock(); } // For resource lookup, we consider a culture the invariant culture by name equality. @@ -1509,24 +1228,17 @@ namespace System.Globalization { // If lcid is -1, use the altName and create one of those special SQL cultures. internal static CultureInfo GetCultureInfoHelper(int lcid, string name, string altName) { - // There is a race condition in this code with the side effect that the second thread's value - // clobbers the first in the dictionary. This is an acceptable race condition since the CultureInfo objects - // are content equal (but not reference equal). Since we make no guarantees there, this race condition is - // acceptable. - // See code:Dictionary#DictionaryVersusHashtableThreadSafety for details on Dictionary versus - // Hashtable thread safety. - // retval is our return value. CultureInfo retval; // Temporary hashtable for the names. - Hashtable tempNameHT = s_NameCachedCultures; + StringCultureInfoDictionary tempNameHT = s_NameCachedCultures; if (name != null) { name = CultureData.AnsiToLower(name); } - + if (altName != null) { altName = CultureData.AnsiToLower(altName); @@ -1535,55 +1247,56 @@ namespace System.Globalization { // We expect the same result for both hashtables, but will test individually for added safety. if (tempNameHT == null) { - tempNameHT = Hashtable.Synchronized(new Hashtable()); + tempNameHT = new StringCultureInfoDictionary(); } else { // If we are called by name, check if the object exists in the hashtable. If so, return it. - if (lcid == -1) + if (lcid == -1 || lcid == 0) { - retval = (CultureInfo)tempNameHT[name + '\xfffd' + altName]; - if (retval != null) + bool ret; + lock (_lock) { - return retval; + ret = tempNameHT.TryGetValue(lcid == 0 ? name : name + '\xfffd' + altName, out retval); } - } - else if (lcid == 0) - { - retval = (CultureInfo)tempNameHT[name]; - if (retval != null) + + if (ret && retval != null) { return retval; } } } -#if FEATURE_USE_LCID + // Next, the Lcid table. - Hashtable tempLcidHT = s_LcidCachedCultures; + StringLcidDictionary tempLcidHT = s_LcidCachedCultures; if (tempLcidHT == null) { // Case insensitive is not an issue here, save the constructor call. - tempLcidHT = Hashtable.Synchronized(new Hashtable()); + tempLcidHT = new StringLcidDictionary(); } else { // If we were called by Lcid, check if the object exists in the table. If so, return it. if (lcid > 0) { - retval = (CultureInfo) tempLcidHT[lcid]; - if (retval != null) + bool ret; + lock (_lock) + { + ret = tempLcidHT.TryGetValue(lcid, out retval); + } + if (ret && retval != null) { return retval; } } } -#endif + // We now have two temporary hashtables and the desired object was not found. // We'll construct it. We catch any exceptions from the constructor call and return null. try { - switch(lcid) + switch (lcid) { case -1: // call the private constructor @@ -1595,65 +1308,55 @@ namespace System.Globalization { break; default: -#if FEATURE_USE_LCID retval = new CultureInfo(lcid, false); break; -#else - return null; -#endif } } - catch(ArgumentException) + catch (ArgumentException) { return null; } // Set it to read-only - retval.m_isReadOnly = true; + retval._isReadOnly = true; if (lcid == -1) { - // This new culture will be added only to the name hash table. - tempNameHT[name + '\xfffd' + altName] = retval; - + lock (_lock) + { + // This new culture will be added only to the name hash table. + tempNameHT[name + '\xfffd' + altName] = retval; + } // when lcid == -1 then TextInfo object is already get created and we need to set it as read only. retval.TextInfo.SetReadOnlyState(true); } - else + else if (lcid == 0) { // Remember our name (as constructed). Do NOT use alternate sort name versions because // we have internal state representing the sort. (So someone would get the wrong cached version) - string newName = CultureData.AnsiToLower(retval.m_name); - - // We add this new culture info object to both tables. - tempNameHT[newName] = retval; -#if FEATURE_USE_LCID - const int LCID_ZH_CHS_HANS = 0x0004; - const int LCID_ZH_CHT_HANT = 0x7c04; + string newName = CultureData.AnsiToLower(retval._name); - if ((retval.LCID == LCID_ZH_CHS_HANS && newName == "zh-hans") - || (retval.LCID == LCID_ZH_CHT_HANT && newName == "zh-hant")) + // We add this new culture info object to both tables. + lock (_lock) { - // do nothing because we only want zh-CHS and zh-CHT to cache - // by lcid + tempNameHT[newName] = retval; } - else + } + else + { + lock (_lock) { - tempLcidHT[retval.LCID] = retval; + tempLcidHT[lcid] = retval; } - -#endif } -#if FEATURE_USE_LCID // Copy the two hashtables to the corresponding member variables. This will potentially overwrite // new tables simultaneously created by a new thread, but maximizes thread safety. - if(-1 != lcid) + if (-1 != lcid) { // Only when we modify the lcid hash table, is there a need to overwrite. s_LcidCachedCultures = tempLcidHT; } -#endif s_NameCachedCultures = tempNameHT; @@ -1661,7 +1364,6 @@ namespace System.Globalization { return retval; } -#if FEATURE_USE_LCID // Gets a cached copy of the specified culture from an internal hashtable (or creates it // if not found). (LCID version)... use named version public static CultureInfo GetCultureInfo(int culture) @@ -1669,21 +1371,19 @@ namespace System.Globalization { // Must check for -1 now since the helper function uses the value to signal // the altCulture code path for SQL Server. // Also check for zero as this would fail trying to add as a key to the hash. - if (culture <= 0) { - throw new ArgumentOutOfRangeException(nameof(culture), - Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + if (culture <= 0) + { + throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum); } Contract.Ensures(Contract.Result<CultureInfo>() != null); Contract.EndContractBlock(); CultureInfo retval = GetCultureInfoHelper(culture, null, null); if (null == retval) { - throw new CultureNotFoundException( - nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported")); + throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); } return retval; } -#endif // Gets a cached copy of the specified culture from an internal hashtable (or creates it // if not found). (Named version) @@ -1694,15 +1394,12 @@ namespace System.Globalization { { throw new ArgumentNullException(nameof(name)); } - Contract.Ensures(Contract.Result<CultureInfo>() != null); - Contract.EndContractBlock(); CultureInfo retval = GetCultureInfoHelper(0, name, null); if (retval == null) { throw new CultureNotFoundException( - nameof(name), name, Environment.GetResourceString("Argument_CultureNotSupported")); - + nameof(name), name, SR.Argument_CultureNotSupported); } return retval; } @@ -1712,32 +1409,28 @@ namespace System.Globalization { public static CultureInfo GetCultureInfo(string name, string altName) { // Make sure we have a valid, non-zero length string as name - if (null == name) + if (name == null) { throw new ArgumentNullException(nameof(name)); } - if (null == altName) + if (altName == null) { throw new ArgumentNullException(nameof(altName)); } + Contract.Ensures(Contract.Result<CultureInfo>() != null); Contract.EndContractBlock(); CultureInfo retval = GetCultureInfoHelper(-1, name, altName); if (retval == null) { - throw new CultureNotFoundException(nameof(name) + " or " + nameof(altName), - String.Format( - CultureInfo.CurrentCulture, - Environment.GetResourceString("Argument_OneOfCulturesNotSupported"), - name, - altName)); + throw new CultureNotFoundException("name or altName", + SR.Format(SR.Argument_OneOfCulturesNotSupported, name, altName)); } return retval; } - // This function is deprecated, we don't like it public static CultureInfo GetCultureInfoByIetfLanguageTag(string name) { @@ -1746,105 +1439,19 @@ namespace System.Globalization { // Disallow old zh-CHT/zh-CHS names if (name == "zh-CHT" || name == "zh-CHS") { - throw new CultureNotFoundException( - nameof(name), - String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureIetfNotSupported"), name) - ); + throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name)); } - + CultureInfo ci = GetCultureInfo(name); // Disallow alt sorts and es-es_TS if (ci.LCID > 0xffff || ci.LCID == 0x040a) { - throw new CultureNotFoundException( - nameof(name), - String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureIetfNotSupported"), name) - ); + throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name)); } - - return ci; - } - - private static volatile bool s_isTaiwanSku; - private static volatile bool s_haveIsTaiwanSku; - internal static bool IsTaiwanSku - { - get - { - if (!s_haveIsTaiwanSku) - { - s_isTaiwanSku = (GetSystemDefaultUILanguage() == "zh-TW"); - s_haveIsTaiwanSku = true; - } - return (bool)s_isTaiwanSku; - } - } - - // - // Helper Methods. - // - - // Get Locale Info Ex calls. So we don't have to muck with the different int/string return types we declared two of these: - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern String nativeGetLocaleInfoEx(String localeName, uint field); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int nativeGetLocaleInfoExInt(String localeName, uint field); - - private static String GetDefaultLocaleName(int localeType) - { - Debug.Assert(localeType == LOCALE_USER_DEFAULT || localeType == LOCALE_SYSTEM_DEFAULT, "[CultureInfo.GetDefaultLocaleName] localeType must be LOCALE_USER_DEFAULT or LOCALE_SYSTEM_DEFAULT"); - - string localeName = null; - if(InternalGetDefaultLocaleName(localeType, JitHelpers.GetStringHandleOnStack(ref localeName))) - { - return localeName; - } - return string.Empty; - } - - // Get the default locale name - [SuppressUnmanagedCodeSecurity] - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalGetDefaultLocaleName(int localetype, StringHandleOnStack localeString); - - private static String GetUserDefaultUILanguage() - { - string userDefaultUiLanguage = null; - if(InternalGetUserDefaultUILanguage(JitHelpers.GetStringHandleOnStack(ref userDefaultUiLanguage))) - { - return userDefaultUiLanguage; - } - return String.Empty; - } - - // Get the user's default UI language, return locale name - [SuppressUnmanagedCodeSecurity] - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalGetUserDefaultUILanguage(StringHandleOnStack userDefaultUiLanguage); - - private static String GetSystemDefaultUILanguage() - { - string systemDefaultUiLanguage = null; - if(InternalGetSystemDefaultUILanguage(JitHelpers.GetStringHandleOnStack(ref systemDefaultUiLanguage))) - { - return systemDefaultUiLanguage; - } - return String.Empty; + return ci; } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [SuppressUnmanagedCodeSecurity] - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalGetSystemDefaultUILanguage(StringHandleOnStack systemDefaultUiLanguage); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern String[] nativeGetResourceFallbackArray(); } } |