Code:
/ FXUpdate3074 / FXUpdate3074 / 1.1 / untmp / whidbey / QFE / ndp / clr / src / BCL / System / Globalization / CultureTableRecord.cs / 12 / CultureTableRecord.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== namespace System.Globalization { using System; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Collections; using Microsoft.Win32.SafeHandles; #if DICTIONARY_FOR_CULTURE_CACHE using System.Collections.Generic; #endif // DICTIONARY_FOR_CULTURE_CACHE using System.Runtime.Serialization; using System.Runtime.Versioning; // Enum for the IFLAGS field [Flags] internal enum CultureFlags { IsSpecificCulture = 0x0001, } /*============================================================================== * * Data record for CultureInfo classes. Used by System.Globalization.CultureInfo. * * */ // Only statics, does not need to be marked with the serializable attribute internal class CultureTableRecord { // For spanish sorting internal const int SPANISH_TRADITIONAL_SORT = 0x040a; private const int SPANISH_INTERNATIONAL_SORT = 0x0c0a; // Sizes defined by the RFC3066 spec private const int MAXSIZE_LANGUAGE = 8; private const int MAXSIZE_REGION = MAXSIZE_LANGUAGE; private const int MAXSIZE_SUFFIX = 8 * MAXSIZE_LANGUAGE; private const int MAXSIZE_FULLTAGNAME = MAXSIZE_LANGUAGE + MAXSIZE_REGION + MAXSIZE_SUFFIX + 4; // The 2 is for the tags and the prefix private static Object s_InternalSyncObject; private static Object InternalSyncObject { get { if (s_InternalSyncObject == null) { Object o = new Object(); Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); } return s_InternalSyncObject; } } // // CultureTableRecordCache caches all CultureTableRecord created objects except the objects created by // RegionInfo constructor which takes region name and instead will be cached in CultureTableRecordRegionCache // private static Hashtable CultureTableRecordCache; private static Hashtable CultureTableRecordRegionCache; #if !FEATURE_PAL #if DICTIONARY_FOR_CULTURE_CACHE // // because the synthetic culture creation is very expensive in term of speed and space. so SyntheticDataCache will be used to cache // the created synthetic culture data so subsequent calls will not need to repeat the creation process especially when creating the // same culture using "new CultureInfo(...)" as the regular cultures created this way is not cached. // The cache is mapping from lcid to the unmanaged allocated safe memory handle that contains the culture data. // //private static Dictionary SyntheticDataCache; internal static Dictionary SyntheticLcidToNameCache; internal static Dictionary SyntheticNameToLcidCache; #else private static Hashtable SyntheticDataCache; internal static Hashtable SyntheticLcidToNameCache; internal static Hashtable SyntheticNameToLcidCache; #endif // DICTIONARY_FOR_CULTURE_CACHE #endif // !FEATURE_PAL // CultureTable this data refers to. private CultureTable m_CultureTable; private unsafe CultureTableData* m_pData; private unsafe ushort* m_pPool; private bool m_bUseUserOverride; private int m_CultureID; private String m_CultureName; private int m_ActualCultureID = 0; private string m_ActualName = null; // // m_synthetic will be true only if we have synthetic culture or synthetic replacement culture. private bool m_synthetic = false; private AgileSafeNativeMemoryHandle nativeMemoryHandle; // private string m_windowsPath = null; private const int LOCALE_SLANGUAGE = 0x00000002; // localized name of language private const int LOCALE_S---- = 0x00000006; // localized name of ---- private const int LOCALE_SNATIVELANGNAME = 0x00000004; // native name of language private const int LOCALE_SNATIVECTRYNAME = 0x00000008; // native name of ---- private const int LOCALE_ICALENDARTYPE = 0x00001009; // iCalendarType type of calendar //////////////////////////////////////////////////////////////////////// // // Create a CultureTable from the given custom/replacement culture name. // // SECURITY SECURITY SECURITY // Before call this function, call ValidateCulturePieceToLower() to verify // that the name does not contain illegal characters (such as "." or backslash. // //////////////////////////////////////////////////////////////////////// [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private unsafe CultureTable GetCustomCultureTable(string name) { CultureTable cultureTable = null; string customCultureFile = GetCustomCultureFile(name); if (customCultureFile == null) { return null; } try { cultureTable = new CultureTable(customCultureFile, false); if (!cultureTable.IsValid) { // If we have invalid culture table then we have custom culture. in that case we'll try // to see if the culture name is one of the framework or synthetic cultures and then // try to create it otherwise we'll throw. String defaultTableActualName; int defaultTableCultureID; int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName( name, out defaultTableCultureID, out defaultTableActualName); if (defaultTableDataItem < 0) // not built in framework culture { #if !FEATURE_PAL InitSyntheticMapping(); BCLDebug.Assert(CultureTableRecord.SyntheticLcidToNameCache != null, "[CultureTableRecord::GetCustomCultureTable] cache should be available now."); if (CultureTableRecord.SyntheticNameToLcidCache[name] == null) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Arg_CorruptedCustomCultureFile"), name)); } #endif // !FEATURE_PAL } return null; // returning null means fallback to framework or synthetic culture. } } catch (FileNotFoundException) { // // getting here means custom culture file get unregistered/renamed from different AppDomain/Process. // just update the cache to point to the empty string as subsequent calls will not bother trying again. // cultureTable = null; } return cultureTable; } //////////////////////////////////////////////////////////////////////// // // Using the specified replacementCultureName, internal unsafe CultureTable TryCreateReplacementCulture(String replacementCultureName, out int dataItem) { string name = ValidateCulturePieceToLower(replacementCultureName, "cultureName", MAXSIZE_FULLTAGNAME); // Before call this function, call ValidateCulturePieceToLower() to verify // that the name does not contain illegal characters (such as "." or backslash. CultureTable cultureTable = GetCustomCultureTable(name); if (cultureTable == null) { dataItem = -1; return (null); } // We have a replacement culture. Use it. int tempID; String tempName; dataItem = cultureTable.GetDataItemFromCultureName(name, out tempID, out tempName); return (dataItem >= 0 ? cultureTable : null); } #if !FEATURE_PAL // //////////////////////////////////////////////////////////////////////// // // Create the hashtable for mapping synthetic culture names to LCID, // and hashtable for mapping synthetic culture LCID to names if they // are not yet created. // //////////////////////////////////////////////////////////////////////// internal static void InitSyntheticMapping() { // #endif // // GetCultureTableRecord create CultureTableRecord object for specific culture name. // This method uses CultureTableRecordCache to make sure we don't have to create this // object if it is already created before. // internal static CultureTableRecord GetCultureTableRecord(string name, bool useUserOverride) { BCLDebug.Assert(name != null, "[CultureTableRecord::GetCultureTableRecord] name should be valid."); // Make sure the cache is valid. if (CultureTableRecordCache == null) { if (name.Length == 0) // Invariant culture { // First time Invariant culture get created we ignore creating the cache return new CultureTableRecord(name, useUserOverride); } lock (InternalSyncObject) { if (CultureTableRecordCache == null) CultureTableRecordCache = new Hashtable(); } } name = ValidateCulturePieceToLower(name, "name", MAXSIZE_FULLTAGNAME); CultureTableRecord [] cultureRecordArray = (CultureTableRecord []) CultureTableRecordCache[name]; if (cultureRecordArray != null) { int index = useUserOverride ? 0 : 1; if (cultureRecordArray[index] == null) { int filled = index == 0 ? 1 : 0; cultureRecordArray[index] = (CultureTableRecord) cultureRecordArray[filled].CloneWithUserOverride(useUserOverride); } return cultureRecordArray[index]; } CultureTableRecord cultureRecord = new CultureTableRecord(name, useUserOverride); lock (InternalSyncObject) { if (CultureTableRecordCache[name] == null) { cultureRecordArray = new CultureTableRecord[2]; cultureRecordArray[useUserOverride ? 0 : 1] = cultureRecord; CultureTableRecordCache[name] = cultureRecordArray; } } return cultureRecord; } // // GetCultureTableRecord create CultureTableRecord object for specific culture Id. // This method convert the culture Id to culture name and then uses GetCultureTableRecord // to get the CultureTableRecord object. // internal static CultureTableRecord GetCultureTableRecord(int cultureId, bool useUserOverride) { if (cultureId == CultureInfo.LOCALE_INVARIANT) return GetCultureTableRecord("", false); String name = null; if (CultureTable.Default.GetDataItemFromCultureID(cultureId, out name) < 0) { #if !FEATURE_PAL // Try ELK if (CultureInfo.IsValidLCID(cultureId, CultureInfo.LCID_INSTALLED)) { InitSyntheticMapping(); name = (String) SyntheticLcidToNameCache[cultureId]; } #endif // !FEATURE_PAL } if (name != null && name.Length>0) // GetDataItemFromCultureID can set the name to empty string. { return GetCultureTableRecord(name, useUserOverride); } throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureId), "culture"); } // // GetCultureTableRecordForRegion create CultureTableRecord object for specific region name. // this method do the following // o try to get the object from the cache. if found then return otherwise try to create it. // o it try to get the record from the framework culture table. if found it then create // the CultureTableRecord object and store it in the cache then return. // o call GetCultureTableRecord to get the object. if found it then store it in the cache // and return. notice that GetCultureTableRecord will try the custom culture then synthetic // culture. // o otherwise we'll throw ArgumentException. // internal static CultureTableRecord GetCultureTableRecordForRegion(string regionName, bool useUserOverride) { BCLDebug.Assert(regionName != null, "[CultureTableRecord::GetCultureTableRecordForRegion] regionName should be valid."); // Make sure the cache is valid. if (CultureTableRecordRegionCache == null) { lock (InternalSyncObject) { if (CultureTableRecordRegionCache == null) CultureTableRecordRegionCache = new Hashtable(); } } regionName = ValidateCulturePieceToLower(regionName, "regionName", MAXSIZE_FULLTAGNAME); CultureTableRecord [] cultureRecordArray = (CultureTableRecord []) CultureTableRecordRegionCache[regionName]; if (cultureRecordArray != null) { int index = useUserOverride ? 0 : 1; if (cultureRecordArray[index] == null) { cultureRecordArray[index] = cultureRecordArray[index == 0 ? 1 : 0].CloneWithUserOverride(useUserOverride); } return cultureRecordArray[index]; } int dataItem = CultureTable.Default.GetDataItemFromRegionName(regionName); CultureTableRecord cultureRecord = null; if (dataItem > 0) { cultureRecord = new CultureTableRecord(regionName, dataItem, useUserOverride); } else { try { cultureRecord = GetCultureTableRecord(regionName, useUserOverride); } catch (ArgumentException) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidRegionName"), regionName), "name"); } } BCLDebug.Assert(cultureRecord != null, "[CultureTableRecord::GetCultureTableRecordForRegion] cultureRecord should be valid."); lock (InternalSyncObject) { if (CultureTableRecordRegionCache[regionName] == null) { cultureRecordArray = new CultureTableRecord[2]; cultureRecordArray[useUserOverride ? 0 : 1] = cultureRecord.CloneWithUserOverride(useUserOverride); CultureTableRecordRegionCache[regionName] = cultureRecordArray; } } return cultureRecord; } // // This constructor used only to create a Framework culture. it doesn't create custom // culture nor synthetic culture. // This is used when requesting the native calendar name for a custom culture with // empty string native calendar name. // internal unsafe CultureTableRecord(int cultureId, bool useUserOverride) { this.m_bUseUserOverride = useUserOverride; int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureID(cultureId, out m_ActualName); if (defaultTableDataItem < 0) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureId), "culture"); } m_ActualCultureID = cultureId; m_CultureTable = CultureTable.Default; m_pData = (CultureTableData*)(m_CultureTable.m_pItemData + m_CultureTable.m_itemSize * defaultTableDataItem); m_pPool = m_CultureTable.m_pDataPool; m_CultureName = SNAME; m_CultureID = (cultureId == SPANISH_TRADITIONAL_SORT) ? cultureId : ILANGUAGE; BCLDebug.Assert(!IsCustomCulture , "[CultureTableRecord::ctor] we shouldn't have custom culture."); BCLDebug.Assert(!IsSynthetic, "[CultureTableRecord::ctor] we shouldn't have synthetic culture."); } // // m_bUseUserOverride indicates that if we need to // // Search order for creating a culture. // /* First, search by name if this is a known culture name from culture.nlp, and it is an alternative sort name (such as de-DE_phoneb) { Get the name from the LANGID by removing the sort ID (so the name becomes de-DE). This is the name used for search replacment culture. } Check if this specified name has a custom/replacement culture file. if there is a custom/replacement culture file { // This is a custom culture, or a replacement culture. return; [CUSTOM/REPLACEMENT CULTURE (.NET CULTURE/SYNTHETIC CULTURE) FOUND BY NAME] } From culture.nlp, */ // // * IMPORTANT * cultureName should be in lower case. // private unsafe CultureTableRecord(String cultureName, bool useUserOverride) { BCLDebug.Assert(cultureName != null, "[CultureTableRecord::ctor] cultureName should be valid."); int cultureID = 0; // Special case for invariant name if (cultureName.Length == 0) { useUserOverride = false; cultureID = CultureInfo.LOCALE_INVARIANT; } this.m_bUseUserOverride = useUserOverride; // We prefer to look up by name (if available) int iDataItem = -1; if (cultureName.Length > 0) { // Check if this is an alternative sort name. String defaultTableActualName; int defaultTableCultureID; string name = cultureName; int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName(name, out defaultTableCultureID, out defaultTableActualName); if (defaultTableDataItem >= 0 && (CultureInfo.GetSortID(defaultTableCultureID) > 0 || defaultTableCultureID == SPANISH_TRADITIONAL_SORT)) { String replacmentCultureName; int nonSortId; if (defaultTableCultureID == SPANISH_TRADITIONAL_SORT) nonSortId = SPANISH_INTERNATIONAL_SORT; else nonSortId = CultureInfo.GetLangID(defaultTableCultureID); // This is an alternative sort culture. if (CultureTable.Default.GetDataItemFromCultureID(nonSortId, out replacmentCultureName) >= 0) { // This is the replacement culture name for an alternative sort. name = ValidateCulturePieceToLower(replacmentCultureName, "cultureName", MAXSIZE_FULLTAGNAME); } } // If the compatibility flag is defined and culture is replacemet culture then we don't // open the custom culture file. instead we'll try to get framework/OS culture. if (!Environment.GetCompatibilityFlag(CompatibilityFlag.DisableReplacementCustomCulture) || IsCustomCultureId(defaultTableCultureID)) { // we always try the replacement custom cultures first. // Before call this function, call ValidateCulturePieceToLower() to verify // that the name does not contain illegal characters (such as "." or backslash. m_CultureTable = GetCustomCultureTable(name); } if (m_CultureTable != null) { // // [CUSTOM/REPLACEMENT CULTURE (.NET CULTURE/SYNTHETIC CULTURE) FOUND BY NAME] // iDataItem = this.m_CultureTable.GetDataItemFromCultureName(name, out this.m_ActualCultureID, out this.m_ActualName); if (defaultTableDataItem >= 0) { // This is a replacment culture (since defaultTableDataItem >= 0), use the default ID/Name from the table. // For de-DE_phoneb, this will set the the actualCultureID to be 0x10407, instead of the LCID for replacment cutlure 0x0407. this.m_ActualCultureID = defaultTableCultureID; this.m_ActualName = defaultTableActualName; } } if (iDataItem < 0 && defaultTableDataItem >= 0) { // // [NON-REPLACEMENT .NET CULTURE FOUND BY NAME] // this.m_CultureTable = CultureTable.Default; this.m_ActualCultureID = defaultTableCultureID; this.m_ActualName = defaultTableActualName; iDataItem = defaultTableDataItem; } #if !FEATURE_PAL // If not exist in main table then try the synthetic one. if (iDataItem < 0) { InitSyntheticMapping(); BCLDebug.Assert(SyntheticNameToLcidCache != null, "[CultureTableRecord::ctor] cache should be created any way."); if (SyntheticNameToLcidCache[name] != null) { // Initialize the lcid so it get created later when trying the creation using the lcid. cultureID = (int) SyntheticNameToLcidCache[name]; // // [SYNTHETIC CULTURE FOUND BY NAME] // } } #endif // !FEATURE_PAL } // If we couldn't get it by name, try culture ID. if (iDataItem < 0 && cultureID > 0) { if (cultureID == CultureInfo.LOCALE_INVARIANT) { // Special case for the Invariant culture. iDataItem = CultureTable.Default.GetDataItemFromCultureID(cultureID, out this.m_ActualName); if (iDataItem > 0) { m_ActualCultureID = cultureID; m_CultureTable = CultureTable.Default; } } #if !FEATURE_PAL else { BCLDebug.Assert(CultureInfo.IsValidLCID(cultureID, CultureInfo.LCID_INSTALLED), "[CultureTableRecord::ctor] culture should be valid ELK culture."); BCLDebug.Assert(SyntheticNameToLcidCache != null, "[CultureTableRecord::ctor] cache should be valid."); // Check if we have replacement cutlure for this synthetic culture. // we always try the replacement custom cultures first. CultureTable replacementTable = null; String replacementCultureName = null; // // Here is the logic for creating the synthetic cultures. // Note the code is optimized to prevent any duplication. // // If (cultureID is Sort Id) // If (non sort Id is one of the framework culture) // If (replacement) // Get data info from custom culture // Else // Get culture data from the framework // Else if (non sort Id one of the cached synthetic cultures) // If (replacement) // Get data info from custom culture // Else // Get culture data from the OS. // Else // Throw exception. // Else // if (cultureID is one of the cached synthetic cultures) // If (replacement) // Get data info from custom culture // Else // Get culture data from the OS. // Else // Throw exception. if (CultureInfo.GetSortID(cultureID) > 0) { iDataItem = CultureTable.Default.GetDataItemFromCultureID( CultureInfo.GetLangID(cultureID), out replacementCultureName); } if (iDataItem < 0) { // Get synthetic culture name from language ID. This should always succeed because this is a valid LCID. // Note that language ID, instead of LCID, since alternative sort uses the same replacement file as default sort. replacementCultureName = (String)SyntheticLcidToNameCache[CultureInfo.GetLangID(cultureID)]; } String actualName = (String)SyntheticLcidToNameCache[cultureID]; int replacementDataItem = -1; if (actualName != null && replacementCultureName != null && !Environment.GetCompatibilityFlag(CompatibilityFlag.DisableReplacementCustomCulture)) { replacementTable = TryCreateReplacementCulture(replacementCultureName, out replacementDataItem); } if (replacementTable == null) { if (iDataItem > 0) { // This is a synthetic culture corresponds to a new OS alternative sort for one of the framework cultures. // example ja-JP_radstr m_CultureTable = CultureTable.Default; m_ActualCultureID = cultureID; m_synthetic = true; // if we are running on Vista nativeGetCultureName will return the normalized name. otherwise // it will return null and then m_ActualName will initialized later with SNAME. m_ActualName = (string) CultureInfo.nativeGetCultureName(cultureID, true, false); } else //if (SyntheticLcidToNameCache == null || SyntheticNameToLcidCache == null) { CacheSyntheticNameLcidMapping(); } } // if (GetSyntheticCulture(cultureID)) { BCLDebug.Assert(m_pData != null && m_pPool != null, "[CultureTableRecord::ctor] Got synthetic culture data and didn't intialize the data pointers."); // // [NON-REPLACEMENT SYNTHETIC CULTURE] // return; } // iDataItem is -1. Let the code fall thru and we will throw exception. } else { // // [REPLACEMENT SYNTHETIC CULTURE] // this.m_CultureTable = replacementTable; iDataItem = replacementDataItem; // Don't initialize m_ActualName by actualName now as we need to have the name normalized. // later it'll be initialized with SNAME which is normalized name. // But if we are running on Vista nativeGetCultureName will return the normalized name. otherwise // it will return null and then m_ActualName will initialized later with SNAME. m_ActualName = (string) CultureInfo.nativeGetCultureName(cultureID, true, false); this.m_ActualCultureID= cultureID; } } #endif // !FEATURE_PAL } // If we found one, use it and return if (iDataItem >= 0) { // Found it, use it this.m_pData = (CultureTableData*)(this.m_CultureTable.m_pItemData + this.m_CultureTable.m_itemSize * iDataItem); this.m_pPool = this.m_CultureTable.m_pDataPool; // Use name & ID from the file ('cept spanish traditional, which has to stay the same) this.m_CultureName = this.SNAME; this.m_CultureID = (m_ActualCultureID == SPANISH_TRADITIONAL_SORT) ? m_ActualCultureID : this.ILANGUAGE; #if !FEATURE_PAL CheckCustomSynthetic(); #endif // !FEATURE_PAL return; } // Error, if we have a name throw that name if (cultureName != null) throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidCultureName"), cultureName), "name"); // No name, throw the LCID throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureID), "culture"); } // // this constructor will create the CultureTableRecord object and point to the // culture table at the slot dataItem. // private unsafe CultureTableRecord(string regionName, int dataItem, bool useUserOverride) { BCLDebug.Assert(regionName != null && regionName.Length > 0, "[CultureTableRecord.CultureTableRecord(regionName,bool)]Expected non-null/empty name"); BCLDebug.Assert(dataItem > 0, "[CultureTableRecord.CultureTableRecord(regionName, dataItem, bool)] dataItem > 0 should be true."); // Assuming it works we'll want these this.m_bUseUserOverride = useUserOverride; this.m_CultureName = regionName; this.m_CultureTable = CultureTable.Default; // Found it, use it this.m_pData = (CultureTableData*)(this.m_CultureTable.m_pItemData + this.m_CultureTable.m_itemSize * dataItem); this.m_pPool = this.m_CultureTable.m_pDataPool; // Use ID from the file this.m_CultureID = this.ILANGUAGE; } #if !FEATURE_PAL // private void CheckCustomSynthetic() { if (IsCustomCulture) { InitSyntheticMapping(); BCLDebug.Assert(SyntheticLcidToNameCache != null, "[CultureTableRecord::CheckCustomSynthetic] cache should be available now."); if (IsCustomCultureId(m_CultureID)) { // Supplemental string name = ValidateCulturePieceToLower(m_CultureName, "CultureName", MAXSIZE_FULLTAGNAME); if (SyntheticNameToLcidCache[name] != null) { m_synthetic = true; m_ActualCultureID = m_CultureID = (int) SyntheticNameToLcidCache[name]; } } else { // Replacement if (SyntheticLcidToNameCache[m_CultureID] != null) { m_synthetic = true; m_ActualCultureID = m_CultureID; } else if (m_CultureID != m_ActualCultureID && SyntheticLcidToNameCache[m_ActualCultureID] != null) { // synthetic alternative sort. m_synthetic = true; } } } } #endif // !FEATURE_PAL internal static void ResetCustomCulturesCache() { #if !FEATURE_PAL CultureTableRecordCache = null; CultureTableRecordRegionCache = null; #endif // !FEATURE_PAL } #if !FEATURE_PAL // // GetScriptTag is used to detect the script of a given culture to be concatenated to the // culture name. the way to do that is to get the sort key of some culture native name as // we used native day name and then from the sort key we get the script. // // Important: this method should return the script tag in lowercase letters. // // private static unsafe bool GetScriptTag(int lcid, out string script) { script = null; string nativeDayName = (string) CultureInfo.nativeGetCultureName(lcid, false, true); if (nativeDayName == null) { return false; } // // Try first the scripts that is not supported by the sort key. // for (int j=0; j = 0x1401 && nativeDayName[j] <= 0x1676) // Canadian Syllabics { script = "cans"; return true; } if (nativeDayName[j] >= 0x1200 && nativeDayName[j] <= 0x137C) // Ethiopic { script = "ethi"; return true; } if (nativeDayName[j] >= 0x1800 && nativeDayName[j] <= 0x1819) // Mongolian { script = "mong"; return true; } if (nativeDayName[j] >= 0xA000 && nativeDayName[j] <= 0xA4C6) // Yi { script = "yiii"; return true; } if (nativeDayName[j] >= 0x13A0 && nativeDayName[j] <= 0x13F4) // Cherokee { script = "cher"; return true; } if (nativeDayName[j] >= 0x1780 && nativeDayName[j] <= 0x17F9) // Khmer { script = "khmr"; return true; } } byte [] sortKey; int length = CultureInfo.GetNativeSortKey(lcid, 0, nativeDayName, nativeDayName.Length, out sortKey); if (length == 0) { return false; } int i=0; while (i // IsBuiltInCulture is used when caching the name and lcid mapping to detect if it is // a framework culture or synthetic cultures. private static bool IsBuiltInCulture(int lcid) { return CultureTable.Default.IsExistingCulture(lcid); } internal static string Concatenate(StringBuilder helper, params string [] stringsToConcat) { BCLDebug.Assert(helper != null, "[CultureTableRecord::Concatenate] string builder should be valid."); if (helper.Length > 0) { helper.Remove(0, helper.Length); } for (int i=0; i internal static bool GetCultureNamesUsingSNameLCType(int [] lcidArray, Hashtable lcidToName, Hashtable nameToLcid) { string cultureName; // first try to see if the LOCALE_SNAME is supported on current platform. cultureName = (string) CultureInfo.nativeGetCultureName(lcidArray[0], true, false); if (cultureName == null) return false; if (!IsBuiltInCulture(lcidArray[0])) { cultureName = ValidateCulturePieceToLower(cultureName, "cultureName", cultureName.Length); nameToLcid[cultureName] = lcidArray[0]; lcidToName[lcidArray[0]] = cultureName; } for (int i=1; i internal static void CacheSyntheticNameLcidMapping() { #if DICTIONARY_FOR_CULTURE_CACHE Dictionary lcidToName = new Dictionary (); Dictionary nameToLcid = new Dictionary (); #else Hashtable lcidToName = new Hashtable(); Hashtable nameToLcid = new Hashtable(); #endif // DICTIONARY_FOR_CULTURE_CACHE // the lock is important here because GetLcidAndCultureNames is using global static variable // which need to be protected accross the threads int [] lcidArray = null; bool result = false; bool tookLock = false; RuntimeHelpers.PrepareConstrainedRegions(); try { // This is intentionally taking a process-global lock, using a CER // to guarantee we release the lock. // Note that we should lock on a internal mscorlib type, so that // no public caller can block us by locking on the same type. Monitor.ReliableEnter(typeof(CultureTableRecord), ref tookLock); // nativeEnumSystemLocale uses a global variable in the native side, so // it needs process-wide synchronization. result = CultureInfo.nativeEnumSystemLocales(out lcidArray); } finally { if (tookLock) Monitor.Exit(typeof(CultureTableRecord)); } if (result) { if (!GetCultureNamesUsingSNameLCType(lcidArray, lcidToName, nameToLcid)) { // Coming here means we couldn't get the names through LOCALE_SNAME so now we'll try to get the names // using the language, ----, and script. // // culturesNeedingScriptInName is the hash which holds all the cultures that need a script tag in the name. // when we encounter a duplicate culture in the nameToLcid table then we know this name will need to have // a script tag. then we remove the name from nameToLcid (to add the one with the script tag instead) // and we add the the removed name to culturesNeedingScriptInName for Hashtable culturesNeedingScriptInName = GetNamesHashtable(); string script; StringBuilder stringHelper = new StringBuilder(); for (int i=0; i struct CompositeCultureData { internal string sname; internal string englishDisplayName; internal string sNativeDisplayName; internal string waCalendars; internal string consoleFallbackName; internal string parentName; internal int parentLcid; } // // AdjustSyntheticCalendars is doing two things. First it ensures the native calendar names are stored at // the correct index in the nativeCalendarNames array. Second it ensure the default calendar in the first // listed one in the waCalendars. private static void AdjustSyntheticCalendars(ref CultureData data, ref CompositeCultureData compositeData) { BCLDebug.Assert(data.waCalendars.Length > 0, "[CultureTableRecord::AdjustSyntheticCalendars] we should have at least one calendar."); StringBuilder calendarsId = new StringBuilder(); int defaultCalendarIndex = 0; ushort maxCalendarId = data.waCalendars[0]; calendarsId.Append((char) maxCalendarId); for (int i=1; i maxCalendarId) maxCalendarId = data.waCalendars[i]; } if (maxCalendarId > 1) { string [] nativeCalendarNames = new string[maxCalendarId]; for (int i=0; i 0) { char defaultCal = calendarsId[defaultCalendarIndex]; calendarsId[defaultCalendarIndex] = calendarsId[0]; calendarsId[0] = defaultCal; } compositeData.waCalendars = calendarsId.ToString(); } //******************************************************************************* // Get the synthetic (ELK) culture data // o it requests the data from the native side // o allocate native memory and fill it // o make m_pData and m_pPool point to the allocated data //******************************************************************************* // [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine)] private unsafe bool GetSyntheticCulture(int cultureID) { if (SyntheticLcidToNameCache == null || SyntheticNameToLcidCache == null) { CacheSyntheticNameLcidMapping(); } if (SyntheticLcidToNameCache[cultureID] == null) { return false; } if (SyntheticDataCache == null) #if DICTIONARY_FOR_CULTURE_CACHE { SyntheticDataCache = new Dictionary (); } #else { SyntheticDataCache = new Hashtable(); } #endif // DICTIONARY_FOR_CULTURE_CACHE else { nativeMemoryHandle = (AgileSafeNativeMemoryHandle) SyntheticDataCache[cultureID]; } if (nativeMemoryHandle != null) { m_pData = (CultureTableData *) nativeMemoryHandle.DangerousGetHandle(); m_pPool = (ushort*) (((byte *) m_pData) + sizeof(CultureTableData)); m_CultureTable = CultureTable.Default; m_CultureName = SNAME; m_CultureID = cultureID; m_synthetic = true; m_ActualCultureID = cultureID; m_ActualName = m_CultureName; return true; } CultureData data = new CultureData(); bool success = false; bool tookLock = false; RuntimeHelpers.PrepareConstrainedRegions(); try { // This is intentionally taking a process-global lock, using a CER // to guarantee we release the lock. // Note that we should lock on a internal mscorlib type, so that // no public caller can block us by locking on the same type. Monitor.ReliableEnter(typeof(CultureTableRecord), ref tookLock); // we should lock on the call of CultureInfo.nativeGetCultureData because it uses native static // variables that are not safe to be accessed in same time from different threads. success = CultureInfo.nativeGetCultureData(cultureID, ref data); } finally { if (tookLock) Monitor.Exit(typeof(CultureTableRecord)); } if (!success) { return false; } CompositeCultureData compositeData = new CompositeCultureData(); int dataSize = GetCultureDataSize(cultureID, ref data, ref compositeData); IntPtr handle = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { handle = Marshal.AllocHGlobal(dataSize); if (handle != IntPtr.Zero) nativeMemoryHandle = new AgileSafeNativeMemoryHandle(handle, true); } } finally { if (nativeMemoryHandle == null && handle != IntPtr.Zero) { // // if we came here means the thread aborted or OOM thrown after allocating our native memory // we need to free this memory before we proceed so we'll not have any memory leaks. // Marshal.FreeHGlobal(handle); handle = IntPtr.Zero; } } if (handle == IntPtr.Zero) { throw new OutOfMemoryException( Environment.GetResourceString("OutOfMemory_MemFailPoint") ); } BCLDebug.Assert(!nativeMemoryHandle.IsInvalid, "[CultureTableRecord::GetSyntheticCulture] The native memory is not allocated."); m_pData = (CultureTableData *) nativeMemoryHandle.DangerousGetHandle(); m_pPool = (ushort*) (((byte *) m_pData) + sizeof(CultureTableData)); uint filledBytes = FillCultureDataMemory(cultureID, ref data, ref compositeData); BCLDebug.Assert(filledBytes == dataSize - sizeof(CultureTableData), String.Format( CultureInfo.CurrentCulture, "[CultureTableRecord::GetSyntheticCulture] The allocated memory size {0} != Filled memory size {1}.", dataSize - sizeof(CultureTableData), filledBytes)); m_CultureTable = CultureTable.Default; m_CultureName = SNAME; m_CultureID = cultureID; m_synthetic = true; m_ActualCultureID = cultureID; m_ActualName = m_CultureName; lock (SyntheticDataCache) { if (SyntheticDataCache[cultureID] == null) { SyntheticDataCache[cultureID] = nativeMemoryHandle; } } BCLDebug.Assert(!nativeMemoryHandle.IsInvalid, "CultureTableData pointer was invalid!"); return true; } /* ELK v1.0 was shipped with wrong ISO language and ---- names for the cultures listed below To support it we need to adjust the names to the correct values. ------------------------------------------------------------------------------------------------------------- LCID Lang Region version Description correct SNAME Wrong SNAME -------------------------------------------------------------------------------------------------------------- 141a bs BA ELK v1 Bosnian (Latin, Bosnia and Herzegovina) bs-BA-Latn (bs-BA) 243b smn FI ELK v1 Sami, Inari (Finland) smn-FI (se-FI) 103b smj NO ELK v1 Sami, Lule (Norway) smj-NO (se-NO) 143b smj SE ELK v1 Sami, Lule (Sweden) smj-SE (se-SE) 203b sms FI ELK v1 Sami, Skolt (Finland) sms-FI (se-FI) 183b sma NO ELK v1 Sami, Southern (Norway) sma-NO (se-NO) 1c3b sma SE ELK v1 Sami, Southern (Sweden) sma-SE (se-SE) 046b quz BO ELK v1 Cusco Quechua (Bolivia) quz-BO (qu-BO) 086b quz EC ELK v1 Cusco Quechua (Equador) quz-EC (qu-EC) 0c6b quz PE ELK v1 Cusco Quechua (Peru) quz-PE (qu-PE) ------------------------------------------------------------------------------------------------------------- */ // internal class AdjustedSyntheticCultureName { internal AdjustedSyntheticCultureName(int lcid, string isoLanguage, string iso----, string sName) { this.lcid = lcid; this.isoLanguage = isoLanguage; this.iso---- = iso----; this.sName = sName; } internal int lcid; internal string isoLanguage; internal string iso----; internal string sName; } // static AdjustedSyntheticCultureName [] s_adjustedSyntheticNames = null; // static AdjustedSyntheticCultureName [] AdjustedSyntheticNames { get { if (s_adjustedSyntheticNames == null) { // // // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT // If you added a new item which has a script tag as part of the name to the AdjustedSyntheticCultureName // array then you need store the culture name (without the script part) into the hashtable in the method // GetNamesHashtable(). // s_adjustedSyntheticNames = new AdjustedSyntheticCultureName[] { new AdjustedSyntheticCultureName(0x141a, "bs", "BA", "bs-Latn-BA" ), // ELK v1 Bosnian (Latin, Bosnia and Herzegovina) (bs-BA) new AdjustedSyntheticCultureName(0x243b, "smn", "FI", "smn-FI" ), // ELK v1 Sami, Inari (Finland) (se-FI) new AdjustedSyntheticCultureName(0x103b, "smj", "NO", "smj-NO" ), // ELK v1 Sami, Lule (Norway) (se-NO) new AdjustedSyntheticCultureName(0x143b, "smj", "SE", "smj-SE" ), // ELK v1 Sami, Lule (Sweden) (se-SE) new AdjustedSyntheticCultureName(0x203b, "sms", "FI", "sms-FI" ), // ELK v1 Sami, Skolt (Finland) (se-FI) new AdjustedSyntheticCultureName(0x183b, "sma", "NO", "sma-NO" ), // ELK v1 Sami, Southern (Norway) (se-NO) new AdjustedSyntheticCultureName(0x1c3b, "sma", "SE", "sma-SE" ), // ELK v1 Sami, Southern (Sweden) (se-SE) new AdjustedSyntheticCultureName(0x046b, "quz", "BO", "quz-BO" ), // ELK v1 Cusco Quechua (Bolivia) (qu-BO) new AdjustedSyntheticCultureName(0x086b, "quz", "EC", "quz-EC" ), // ELK v1 Cusco Quechua (Equador) (qu-EC) new AdjustedSyntheticCultureName(0x0c6b, "quz", "PE", "quz-PE" ) // ELK v1 Cusco Quechua (Peru) (qu-PE) }; } return (s_adjustedSyntheticNames); } } // internal static Hashtable GetNamesHashtable() { Hashtable table = new Hashtable(); // All keys is the hashtable should be in lowercase. table["bs-ba"] = ""; table["tg-tj"] = ""; table["mn-cn"] = ""; table["iu-ca"] = ""; return table; } // internal static void GetAdjustedNames(int lcid, out AdjustedSyntheticCultureName adjustedNames) { for (int i=0; i private unsafe uint FillCultureDataMemory(int cultureID, ref CultureData data, ref CompositeCultureData compositeData) { BCLDebug.Assert(m_pData != null && m_pPool != null, "[CultureTableRecord::FillCultureDataMemory] we should have valid buffers here."); uint offset = 0; Hashtable offsetTable = new Hashtable(30); // Add padding to get our funny dword alignment m_pPool[offset] = 0; offset++; // Set empty string and correct its offset SetPoolString("", offsetTable, ref offset); offsetTable[""] = (uint)0; m_pData->iLanguage = (ushort) cultureID; m_pData->sName = (ushort) SetPoolString(compositeData.sname, offsetTable, ref offset); m_pData->iDigits = (ushort) data.iDigits; m_pData->iNegativeNumber = (ushort) data.iNegativeNumber; m_pData->iCurrencyDigits = (ushort) data.iCurrencyDigits; m_pData->iCurrency = (ushort) data.iCurrency; m_pData->iNegativeCurrency = (ushort) data.iNegativeCurrency; m_pData->iLeadingZeros = (ushort) data.iLeadingZeros; m_pData->iFlags = (ushort) CultureFlags.IsSpecificCulture; m_pData->iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(data.iFirstDayOfWeek); m_pData->iFirstWeekOfYear = (ushort) data.iFirstWeekOfYear; m_pData->iLocale = (ushort) data.iLocale; m_pData->iMeasure = (ushort) data.iMeasure; m_pData->iDigitSubstitution = (ushort) data.iDigitSubstitution; m_pData->waGrouping = (ushort) SetPoolString(data.waGrouping, offsetTable, ref offset); m_pData->waMonetaryGrouping = (ushort) SetPoolString(data.waMonetaryGrouping, offsetTable, ref offset); m_pData->sListSeparator = (ushort) SetPoolString(data.sListSeparator, offsetTable, ref offset); m_pData->sDecimalSeparator = (ushort) SetPoolString(data.sDecimalSeparator, offsetTable, ref offset); m_pData->sThousandSeparator = (ushort) SetPoolString(data.sThousandSeparator, offsetTable, ref offset); m_pData->sCurrency = (ushort) SetPoolString(data.sCurrency, offsetTable, ref offset); m_pData->sMonetaryDecimal = (ushort) SetPoolString(data.sMonetaryDecimal, offsetTable, ref offset); m_pData->sMonetaryThousand = (ushort) SetPoolString(data.sMonetaryThousand, offsetTable, ref offset); m_pData->sPositiveSign = (ushort) SetPoolString(data.sPositiveSign, offsetTable, ref offset); m_pData->sNegativeSign = (ushort) SetPoolString(data.sNegativeSign, offsetTable, ref offset); m_pData->sAM1159 = (ushort) SetPoolString(data.sAM1159, offsetTable, ref offset); m_pData->sPM2359 = (ushort) SetPoolString(data.sPM2359, offsetTable, ref offset); m_pData->saNativeDigits = (ushort) SetPoolStringArrayFromSingleString(data.saNativeDigits, offsetTable, ref offset); m_pData->saTimeFormat = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saTimeFormat); m_pData->saShortDate = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saShortDate); m_pData->saLongDate = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saLongDate); m_pData->saYearMonth = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saYearMonth); m_pData->saDuration = (ushort) SetPoolStringArray(offsetTable, ref offset, ""); m_pData->iDefaultLanguage = m_pData->iLanguage; m_pData->iDefaultAnsiCodePage=(ushort) data.iDefaultAnsiCodePage; m_pData->iDefaultOemCodePage= (ushort) data.iDefaultOemCodePage; m_pData->iDefaultMacCodePage= (ushort) data.iDefaultMacCodePage; m_pData->iDefaultEbcdicCodePage= (ushort) data.iDefaultEbcdicCodePage; m_pData->iGeoId = (ushort) data.iGeoId; m_pData->iPaperSize = (ushort) data.iPaperSize; m_pData->iIntlCurrencyDigits= (ushort) data.iIntlCurrencyDigits; m_pData->iParent = (ushort) compositeData.parentLcid; m_pData->waCalendars = (ushort) SetPoolString(compositeData.waCalendars, offsetTable, ref offset); m_pData->sAbbrevLang = (ushort) SetPoolString(data.sAbbrevLang, offsetTable, ref offset); m_pData->sISO639Language = (ushort) SetPoolString(data.sIso639Language, offsetTable, ref offset); m_pData->sEnglishLanguage = (ushort) SetPoolString(data.sEnglishLanguage, offsetTable, ref offset); m_pData->sNativeLanguage = (ushort) SetPoolString(data.sNativeLanguage, offsetTable, ref offset); m_pData->sEnglish---- = (ushort) SetPoolString(data.sEnglish----, offsetTable, ref offset); m_pData->sNative---- = (ushort) SetPoolString(data.sNative----, offsetTable, ref offset); m_pData->sAbbrev---- = (ushort) SetPoolString(data.sAbbrev----, offsetTable, ref offset); m_pData->sISO3166----Name= (ushort) SetPoolString(data.sIso3166----Name, offsetTable, ref offset); m_pData->sIntlMonetarySymbol= (ushort) SetPoolString(data.sIntlMonetarySymbol, offsetTable, ref offset); m_pData->sEnglishCurrency = (ushort) SetPoolString(data.sEnglishCurrency, offsetTable, ref offset); m_pData->sNativeCurrency = (ushort) SetPoolString(data.sNativeCurrency, offsetTable, ref offset); m_pData->waFontSignature = (ushort) SetPoolString(data.waFontSignature, offsetTable, ref offset); m_pData->sISO639Language2 = (ushort) SetPoolString(data.sISO639Language2, offsetTable, ref offset); m_pData->sISO3166----Name2= (ushort) SetPoolString(data.sISO3166----Name2, offsetTable, ref offset); m_pData->sParent = (ushort) SetPoolString(compositeData.parentName, offsetTable, ref offset); m_pData->saDayNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saDayNames); m_pData->saAbbrevDayNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saAbbrevDayNames); m_pData->saMonthNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saMonthNames); m_pData->saAbbrevMonthNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saAbbrevMonthNames); m_pData->saMonthGenitiveNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saGenitiveMonthNames); m_pData->saAbbrevMonthGenitiveNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saAbbrevGenitiveMonthNames); m_pData->saNativeCalendarNames = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saNativeCalendarNames); // To Do: We need to find a better way to get the alternative sort names. // look at ComNldInfo::nativeGetCultureData and CultureTableRecord.GetCultureDataSize // m_pData->saAltSortID = (ushort) SetPoolStringArray(offsetTable, ref offset, data.saAltSortID); m_pData->saAltSortID = (ushort) SetPoolStringArray(offsetTable, ref offset, ""); m_pData->iNegativePercent = (ushort) CultureInfo.InvariantCulture.NumberFormat.PercentNegativePattern; m_pData->iPositivePercent = (ushort) CultureInfo.InvariantCulture.NumberFormat.PercentPositivePattern; m_pData->iFormatFlags = (ushort) DateTimeFormatFlags.None; // We need to keep this since we need value for TextInfo.IsRightToLeft. m_pData->iLineOrientations = (ushort) 0x0000; m_pData->iTextInfo = m_pData->iLanguage; m_pData->iInputLanguageHandle=m_pData->iLanguage; m_pData->iCompareInfo = m_pData->iLanguage; m_pData->sEnglishDisplayName= (ushort) SetPoolString(compositeData.englishDisplayName, offsetTable, ref offset); m_pData->sNativeDisplayName = (ushort) SetPoolString(compositeData.sNativeDisplayName, offsetTable, ref offset); m_pData->sPercent = (ushort) SetPoolString(CultureInfo.InvariantCulture.NumberFormat.PercentSymbol, offsetTable, ref offset); m_pData->sNaN = (ushort) SetPoolString(data.sNaN, offsetTable, ref offset); m_pData->sPositiveInfinity = (ushort) SetPoolString(data.sPositiveInfinity, offsetTable, ref offset); m_pData->sNegativeInfinity = (ushort) SetPoolString(data.sNegativeInfinity, offsetTable, ref offset); m_pData->sMonthDay = (ushort) SetPoolString(CultureInfo.InvariantCulture.DateTimeFormat.MonthDayPattern, offsetTable, ref offset); m_pData->sAdEra = (ushort) SetPoolString(CultureInfo.InvariantCulture.DateTimeFormat.GetEraName(0), offsetTable, ref offset); m_pData->sAbbrevAdEra = (ushort) SetPoolString(CultureInfo.InvariantCulture.DateTimeFormat.GetAbbreviatedEraName(0), offsetTable, ref offset); m_pData->sRegionName = m_pData->sISO3166----Name; m_pData->sConsoleFallbackName= (ushort) SetPoolString(compositeData.consoleFallbackName, offsetTable, ref offset); m_pData->saShortTime = m_pData->saTimeFormat; m_pData->saSuperShortDayNames= (ushort) SetPoolStringArray(offsetTable, ref offset, data.saSuperShortDayNames); m_pData->saDateWords = m_pData->saDuration; m_pData->sSpecificCulture = m_pData->sName; m_pData->sScripts = 0; // Offset is in words, but we return size in bytes return 2 * offset; } // Sets a string in the pool, returns string offset in words // private unsafe uint SetPoolString(string s, Hashtable offsetTable, ref uint currentOffset) { BCLDebug.Assert((currentOffset & 1) == 1, "[CultureTableRecord.SetPoolString]Expected offset to be on odd word"); uint offset = currentOffset; if (offsetTable[s] == null) { offsetTable[s] = currentOffset; m_pPool[currentOffset] = (ushort) s.Length; currentOffset++; for (int i=0; i private unsafe uint SetPoolStringArray(Hashtable offsetTable, ref uint currentOffset, params string [] array) { BCLDebug.Assert((currentOffset & 1) == 1, "[CultureTableRecord.SetPoolStringArray]Expected offset to be on odd word"); uint [] offsetArray = new uint[array.Length]; for (int i=0; i private unsafe uint SetPoolStringArrayFromSingleString(string s, Hashtable offsetTable, ref uint currentOffset) { BCLDebug.Assert(s.Length==10, "[CultureTableRecord.SetPoolStringArrayFromSingleString]Expect 10 digits in the saNativeDigits string"); String[] strings = new string[s.Length]; for (int i=0; i< s.Length; i++) strings[i] = s.Substring(i,1); return SetPoolStringArray(offsetTable, ref currentOffset, strings); } // private bool NameHasScriptTag(string tempName) { int dashCount = 0; int i= 0; while (i 1); } // // This method construct the culture name (should be called only when we have script tag). // // private static string GetCasedName(string name) { StringBuilder taggedName = new StringBuilder(name.Length); int i = 0; while (i private static void GetSyntheticParentData(ref CultureData data, ref CompositeCultureData compositeData) { compositeData.parentLcid = CultureInfo.InvariantCulture.LCID; compositeData.parentName = CultureInfo.InvariantCulture.Name; if (data.sParentName != null) { // Vista or above string defaultTableActualName; string parentName; int defaultTableCultureID; parentName = ValidateCulturePieceToLower(data.sParentName, "ParentName", MAXSIZE_FULLTAGNAME); int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName(parentName, out defaultTableCultureID, out defaultTableActualName); if (defaultTableDataItem >= 0) { compositeData.parentLcid = defaultTableCultureID; compositeData.parentName = defaultTableActualName; } else if (SyntheticNameToLcidCache[parentName] != null) { compositeData.parentLcid = (int) SyntheticNameToLcidCache[parentName]; compositeData.parentName = data.sParentName; } } } // private static void GetSyntheticConsoleFallback(ref CultureData data, ref CompositeCultureData compositeData) { compositeData.consoleFallbackName = CultureInfo.InvariantCulture.GetConsoleFallbackUICulture().Name; if (data.sConsoleFallbackName != null) { // Vista or above string defaultTableActualName; string consoleFallbackName; int defaultTableCultureID; consoleFallbackName = ValidateCulturePieceToLower(data.sConsoleFallbackName, "ConsoleFallbackName", MAXSIZE_FULLTAGNAME); int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName(consoleFallbackName, out defaultTableCultureID, out defaultTableActualName); if (defaultTableDataItem >= 0) { compositeData.consoleFallbackName = defaultTableActualName; } else if (SyntheticNameToLcidCache[consoleFallbackName] != null) { compositeData.consoleFallbackName = data.sConsoleFallbackName; } } } // Get the pool size of our data in bytes // private unsafe int GetCultureDataSize(int cultureID, ref CultureData data, ref CompositeCultureData compositeData) { int size = sizeof(CultureTableData); Hashtable offsetTable = new Hashtable(30); // Get us odd word aligned so that our DWORD arrays don't get out of alignment size += 2; // CultureTableData.sName (including the hyphen, length, and null termination) size += GetPoolStringSize("", offsetTable); // try to detect if we are in platform that supports LOCALE_SNAME by calling nativeGetCultureName. // if so we don't have to adjust the names. compositeData.sname = CultureInfo.nativeGetCultureName(cultureID, true, false); if (compositeData.sname == null) { AdjustedSyntheticCultureName adjustedNames; GetAdjustedNames(cultureID, out adjustedNames); if (adjustedNames != null) { data.sIso639Language = adjustedNames.isoLanguage; data.sIso3166----Name= adjustedNames.iso----; compositeData.sname = adjustedNames.sName; } else { // this part to make the culture name in proper letter casing. string tempName = (string) SyntheticLcidToNameCache[cultureID]; if ( NameHasScriptTag(tempName) ) { compositeData.sname = GetCasedName(tempName); } else { // construct sname in proper letter casing. compositeData.sname = data.sIso639Language + "-" + data.sIso3166----Name; } } } compositeData.englishDisplayName = data.sEnglishLanguage + " (" + data.sEnglish---- + ")"; compositeData.sNativeDisplayName = data.sNativeLanguage + " (" + data.sNative---- + ")"; // CultureTableData.waCalendars AdjustSyntheticCalendars(ref data, ref compositeData); size += GetPoolStringSize(compositeData.sname, offsetTable); size += GetPoolStringSize(compositeData.englishDisplayName, offsetTable); size += GetPoolStringSize(compositeData.sNativeDisplayName, offsetTable); size += GetPoolStringSize(compositeData.waCalendars, offsetTable); // CultureTableData.sParent GetSyntheticParentData(ref data, ref compositeData); BCLDebug.Assert(compositeData.parentName != null, "[CultureTableRecord::GetCultureDataSize] compositeData.parentName should be valid."); size += GetPoolStringSize(compositeData.parentName, offsetTable); // CultureTableData.sIso639Language size += GetPoolStringSize(data.sIso639Language, offsetTable); // CultureTableData.sListSeparator size += GetPoolStringSize(data.sListSeparator, offsetTable); // CultureTableData.sDecimalSeparator size += GetPoolStringSize(data.sDecimalSeparator, offsetTable); // CultureTableData.sThousandSeparator size += GetPoolStringSize(data.sThousandSeparator, offsetTable); // CultureTableData.sCurrency size += GetPoolStringSize(data.sCurrency, offsetTable); // CultureTableData.sMonetaryDecimal size += GetPoolStringSize(data.sMonetaryDecimal, offsetTable); // CultureTableData.sMonetaryThousand size += GetPoolStringSize(data.sMonetaryThousand, offsetTable); // CultureTableData.sPositiveSign size += GetPoolStringSize(data.sPositiveSign, offsetTable); // CultureTableData.sNegativeSign size += GetPoolStringSize(data.sNegativeSign, offsetTable); // CultureTableData.sAM1159 size += GetPoolStringSize(data.sAM1159, offsetTable); // CultureTableData.sPM2359 size += GetPoolStringSize(data.sPM2359, offsetTable); // CultureTableData.sAbbrevLang size += GetPoolStringSize(data.sAbbrevLang, offsetTable); // CultureTableData.sEnglishLanguage size += GetPoolStringSize(data.sEnglishLanguage, offsetTable); // CultureTableData.sNativeLanguage size += GetPoolStringSize(data.sNativeLanguage, offsetTable); // CultureTableData.sEnglish---- size += GetPoolStringSize(data.sEnglish----, offsetTable); // CultureTableData.sNative---- size += GetPoolStringSize(data.sNative----, offsetTable); // CultureTableData.sAbbrev---- size += GetPoolStringSize(data.sAbbrev----, offsetTable); // CultureTableData.sISO3166----Name size += GetPoolStringSize(data.sIso3166----Name, offsetTable); // CultureTableData.sIntlMonetarySymbol size += GetPoolStringSize(data.sIntlMonetarySymbol, offsetTable); // CultureTableData.sEnglishCurrency size += GetPoolStringSize(data.sEnglishCurrency, offsetTable); // CultureTableData.sNativeCurrency size += GetPoolStringSize(data.sNativeCurrency, offsetTable); // CultureTableData.sPercent size += GetPoolStringSize(CultureInfo.InvariantCulture.NumberFormat.PercentSymbol, offsetTable); // CultureTableData.sNaN if (data.sNaN == null) data.sNaN = CultureInfo.InvariantCulture.NumberFormat.NaNSymbol; size += GetPoolStringSize(data.sNaN, offsetTable); // CultureTableData.sPositiveInfinity if (data.sPositiveInfinity == null) data.sPositiveInfinity = CultureInfo.InvariantCulture.NumberFormat.PositiveInfinitySymbol; size += GetPoolStringSize(data.sPositiveInfinity, offsetTable); // CultureTableData.sNegativeInfinity if (data.sNegativeInfinity == null) data.sNegativeInfinity = CultureInfo.InvariantCulture.NumberFormat.NegativeInfinitySymbol; size += GetPoolStringSize(data.sNegativeInfinity, offsetTable); // CultureTableData.sMonthDay size += GetPoolStringSize(CultureInfo.InvariantCulture.DateTimeFormat.MonthDayPattern, offsetTable); // CultureTableData.sAdEra size += GetPoolStringSize(CultureInfo.InvariantCulture.DateTimeFormat.GetEraName(0), offsetTable); // CultureTableData.sAbbrevAdEra size += GetPoolStringSize(CultureInfo.InvariantCulture.DateTimeFormat.GetAbbreviatedEraName(0), offsetTable); // CultureTableData.sConsoleFallbackName GetSyntheticConsoleFallback(ref data, ref compositeData); BCLDebug.Assert(compositeData.consoleFallbackName != null, "[CultureTableRecord::GetCultureDataSize] compositeData.consoleFallbackName should be valid."); size += GetPoolStringSize(compositeData.consoleFallbackName, offsetTable); // CultureTableData.saMonthNames size += GetPoolStringArraySize(offsetTable, data.saMonthNames); // CultureTableData.saDayNames size += GetPoolStringArraySize(offsetTable, data.saDayNames); // CultureTableData.saAbbrevDayNames size += GetPoolStringArraySize(offsetTable, data.saAbbrevDayNames); // CultureTableData.saAbbrevMonthNames size += GetPoolStringArraySize(offsetTable, data.saAbbrevMonthNames); // CultureTableData.saMonthGenitiveNames data.saGenitiveMonthNames[12] = data.saMonthNames[12]; size += GetPoolStringArraySize(offsetTable, data.saGenitiveMonthNames); // CultureTableData.saAbbrevMonthGenitiveNames data.saAbbrevGenitiveMonthNames[12] = data.saAbbrevMonthNames[12]; size += GetPoolStringArraySize(offsetTable, data.saAbbrevGenitiveMonthNames); // CultureTableData.saNativeCalendarNames size += GetPoolStringArraySize(offsetTable, data.saNativeCalendarNames); // CultureTableData.saTimeFormat size += GetPoolStringArraySize(offsetTable, data.saTimeFormat); // CultureTableData.saShortDate size += GetPoolStringArraySize(offsetTable, data.saShortDate); // CultureTableData.saLongDate size += GetPoolStringArraySize(offsetTable, data.saLongDate); // CultureTableData.saYearMonth size += GetPoolStringArraySize(offsetTable, data.saYearMonth); // used for CultureTableData.saDuration/saDateWords size += GetPoolStringArraySize(offsetTable, ""); // To Do: We need to find a better way to get the alternative sort names. // look at ComNldInfo::nativeGetCultureData and CultureTableRecord.FillCultureDataMemory // size += GetPoolStringArraySize(offsetTable, data.saAltSortID); // CultureTableData.saAltSortID and CultureTableData.saDateWords size += GetPoolStringArraySize(offsetTable, ""); // CultureTableData.waGrouping data.waGrouping = GroupSizesConstruction(data.waGrouping); size += GetPoolStringSize(data.waGrouping, offsetTable); // CultureTableData.waMonetaryGrouping data.waMonetaryGrouping = GroupSizesConstruction(data.waMonetaryGrouping); size += GetPoolStringSize(data.waMonetaryGrouping, offsetTable); // CultureTableData.saNativeDigits size += GetPoolStringArraySize(data.saNativeDigits, offsetTable); // CultureTableData.waFontSignature size += GetPoolStringSize(data.waFontSignature, offsetTable); if (data.sISO3166----Name2 == null) data.sISO3166----Name2 = data.sIso3166----Name; size += GetPoolStringSize(data.sISO3166----Name2, offsetTable); if (data.sISO639Language2 == null) data.sISO639Language2 = data.sIso639Language; size += GetPoolStringSize(data.sISO639Language2, offsetTable); if (data.saSuperShortDayNames == null) data.saSuperShortDayNames = data.saAbbrevDayNames; size += GetPoolStringArraySize(offsetTable, data.saSuperShortDayNames); return size; } // Return the size of a string in the string pool in bytes // This needs to have an even # of words so that any following DWORD // array doesn't get unaligned // private int GetPoolStringSize(string s, Hashtable offsetTable) { // 0 size if we know about it already int size = 0; // See if we already knew about it if (offsetTable[s] == null) { offsetTable[s] = ""; size = 2 * (s.Length + 1 + // adding one for the length (1 - (s.Length & 1))); // Plus one to keep us odd aligned if necessary } BCLDebug.Assert((size & 3) == 0, "[CultureTableRecord.GetPoolStringSize]Didn't maintain dword alignment"); return size; } // Writing every character as seperate string in a string array (size of that array & strings in bytes) // private int GetPoolStringArraySize(string s, Hashtable offsetTable) { // We have to do this the slow way just in case one of these strings is duplicated in the string pool BCLDebug.Assert(s.Length==10, "[CultureTableRecord.GetPoolStringArraySize(string, int)]Expect 10 digits in the saNativeDigits string"); String[] strings = new string[s.Length]; for (int i=0; i< s.Length; i++) strings[i] = s.Substring(i,1); return GetPoolStringArraySize(offsetTable, strings); } // Given array of strings we calculate bytes needed to store them. private int GetPoolStringArraySize(Hashtable offsetTable, params string [] array) { int stringsSize = 0; for (int i=0; i private string GroupSizesConstruction(string rawGroupSize) { int length = rawGroupSize.Length; if ( rawGroupSize[length - 1] == '0' ) { // if Win32 returned zero at the end of the string then weneed to remove it out. length--; } int i=0; StringBuilder groupSize = new StringBuilder(); while (i = '0' && rawGroupSize[i] <= '9', "[CultureTableRecord::GroupSizesConstruction] invalid group size number."); groupSize.Append((char) ((int) rawGroupSize[i] - (int) '0')); i++; if (i 0, "[CultureTableRecord::GroupSizesConstruction] null group sizes."); return (groupSizesString); } #endif // !FEATURE_PAL private String WindowsPath { get { if (m_windowsPath == null) { m_windowsPath = CultureInfo.nativeGetWindowsDirectory(); } return (m_windowsPath); } } /*---------------------------------------------------------- * * Gets a filename of a custom culture covered by the given string. * * Builds a cache of items found (adding String.Empty for files that are not present) * to avoid hitting the disk repeatedly. * *--------------------------------------------------------*/ private string GetCustomCultureFile(string name) { #if !FEATURE_PAL // Calling SHGetFolderPath is expensive. Cache whether // we need to load a data file or not. StringBuilder sbFile = new StringBuilder(WindowsPath); sbFile.Append("\\Globalization\\"); sbFile.Append(name); sbFile.Append(".nlp"); // GetFullPath should not be needed here since a full path is being generated string file = (sbFile.ToString()); bool customCultureExists = CultureInfo.nativeFileExists(file); if (customCultureExists) return(file); #endif // !FEATURE_PAL return(null); } /*---------------------------------------------------------- * * Validate a culture name -- throws if it is not valid. If it is valid, return * the lowercase version of it, suitable for later caching. * *--------------------------------------------------------*/ private static string ValidateCulturePieceToLower(string testString, string paramName, int maxLength) { if(testString.Length > maxLength) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_NameTooLong"), testString, maxLength), paramName); } StringBuilder sb = new StringBuilder(testString.Length); for(int ich = 0; ich < testString.Length; ich++) { char ch = testString[ich]; if(ch <= 'Z' && ch >= 'A') { sb.Append((char)(ch - 'A' + 'a')); } else if(((ch <= 'z' && ch >= 'a') || (ch <= '9' && ch >= '0') || (ch == '_') || (ch == '-'))) { sb.Append(ch); } else { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_NameContainsInvalidCharacters"), testString), paramName); } } return(sb.ToString()); } internal static string AnsiToLower(string testString) { StringBuilder sb = new StringBuilder(testString.Length); for (int ich = 0; ich < testString.Length; ich++) { char ch = testString[ich]; sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch); } return(sb.ToString()); } // internal bool IsSynthetic { get { return m_synthetic; } } internal bool IsCustomCulture { get { // If we came from the assembly we aren't custom return !(this.m_CultureTable.fromAssembly); } } internal bool IsReplacementCulture { get { return ( this.IsCustomCulture && !IsCustomCultureId(m_CultureID) ); } } internal int CultureID { get { BCLDebug.Assert(this.m_CultureID > 0, "[CultureTableRecord.CultureID]unexpected m_CultureId"); return this.m_CultureID; } } internal String CultureName { get { BCLDebug.Assert(this.m_CultureName != null, "[CultureTableRecord.CultureName]unexpected m_CultureName"); return this.m_CultureName; } set { BCLDebug.Assert(value != null, "[CultureTableRecord.CultureName]Expected non-null value for culture name"); this.m_CultureName = value; } } internal bool UseUserOverride { get { return this.m_bUseUserOverride; } } // A property to indicate if we should retrieve information by calling the Win32 GetLocaleInfo(). internal unsafe bool UseGetLocaleInfo { get { if (!this.m_bUseUserOverride) { return (false); } int lcid; CultureInfo.nativeGetUserDefaultLCID(&lcid, CultureInfo.LOCALE_USER_DEFAULT); if (ActualCultureID == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED && lcid == CultureInfo.LOCALE_CUSTOM_DEFAULT) { if (SNAME.Equals(CultureInfo.nativeGetCultureName(lcid, true, false))) { return true; } return false; } return (this.ActualCultureID == lcid); } } // A method to internal unsafe bool UseCurrentCalendar(int calID) { return (UseGetLocaleInfo && CultureInfo.nativeGetCurrentCalendar() == calID); } internal bool IsValidSortID(int sortID) { BCLDebug.Assert(sortID >= 0 && sortID <= 0xffff, "sortID is invalid"); // SortID is 16-bit positive integer. if(sortID == 0 || (this.SALTSORTID != null && this.SALTSORTID.Length >= sortID && this.SALTSORTID[sortID - 1].Length != 0)) { return true; } else { return false; } } internal CultureTableRecord CloneWithUserOverride(bool userOverride) { if (m_bUseUserOverride == userOverride) return this; CultureTableRecord cultureTableRecord = (CultureTableRecord) this.MemberwiseClone(); cultureTableRecord.m_bUseUserOverride = userOverride; return cultureTableRecord; } // // CultureNativeDisplayName called when we need to get the native display name for the culture // from Win32 side. we need to do that in cases like synthetic cultures. // internal unsafe string CultureNativeDisplayName { get { int lcid; CultureInfo.nativeGetUserDefaultUILanguage(&lcid); if (CultureInfo.GetLangID(lcid) == CultureInfo.GetLangID(CultureInfo.CurrentUICulture.LCID)) { string localizedLanguageName = CultureInfo.nativeGetLocaleInfo(m_ActualCultureID, LOCALE_SLANGUAGE); if (localizedLanguageName != null) { // if ( localizedLanguageName[localizedLanguageName.Length - 1] == '\u0000' ) return localizedLanguageName.Substring(0, localizedLanguageName.Length - 1); else return localizedLanguageName; } } return this.SNATIVEDISPLAYNAME; } } // // RegionNativeDisplayName called when we need to get the native display name for the region // from Win32 side. we need to do that in cases like synthetic cultures. // internal unsafe string RegionNativeDisplayName { get { int lcid; CultureInfo.nativeGetUserDefaultUILanguage(&lcid); if (CultureInfo.GetLangID(lcid) == CultureInfo.GetLangID(CultureInfo.CurrentUICulture.LCID)) { string localized----Name = CultureInfo.nativeGetLocaleInfo(m_ActualCultureID, LOCALE_S----); if (localized----Name != null) { if ( localized----Name[localized----Name.Length - 1] == '\u0000' ) return localized----Name.Substring(0, localized----Name.Length - 1); else return localized----Name ; } } return this.SNATIVE----; } } //////////////////////////////////////////////////////////////////////// // // Equals // // Implements Object.Equals(). Returns a boolean indicating whether // or not object refers to the same CultureTableRecord as the current instance. // //////////////////////////////////////////////////////////////////////// public override unsafe bool Equals(Object value) { CultureTableRecord that = value as CultureTableRecord; return (that != null) && (this.m_pData == that.m_pData && this.m_bUseUserOverride == that.m_bUseUserOverride && this.m_CultureID == that.m_CultureID && CultureInfo.InvariantCulture.CompareInfo.Compare( this.m_CultureName, that.m_CultureName, CompareOptions.IgnoreCase ) == 0 && this.m_CultureTable.Equals(that.m_CultureTable) ); } //////////////////////////////////////////////////////////////////////// // // GetHashCode // // Implements Object.GetHashCode(). Returns the hash code for the // CultureInfo. The hash code is guaranteed to be the same for RegionInfo // A and B where A.Equals(B) is true. // //////////////////////////////////////////////////////////////////////// public override int GetHashCode() { //This doesn't tell apart user override from non-user override if (!IsCustomCultureId(m_CultureID)) return (this.m_CultureID); return (this.m_CultureName.GetHashCode()); } // Get a String private unsafe String GetString(uint iOffset) { char* pCharValues = unchecked((char*)(this.m_pPool + iOffset)); // For null strings, iOffset, pPool[0] and pPool[1] are all three 0. // The previous implimentation used [1] to test and I was afraid to change it. if (pCharValues[1] == 0) { BCLDebug.Assert(iOffset == 0, "[CultureTableRecord.GetString]Expected empty strings to have 0 offset"); return String.Empty; } return new String(pCharValues + 1, 0, (int)pCharValues[0]); } private int InteropLCID { get { return ActualCultureID == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED ? CultureInfo.LOCALE_CUSTOM_DEFAULT : ActualCultureID; } } private String GetOverrideString(uint iOffset, int iWindowsFlag) { #if !FEATURE_PAL if (this.UseGetLocaleInfo) { // Get User Override value if necessary String value = CultureInfo.nativeGetLocaleInfo(InteropLCID, iWindowsFlag); if (value != null && value.Length > 0) return value; } #endif return GetString(iOffset); } private unsafe String[] GetStringArray(uint iOffset) { if (iOffset == 0) return new String[0]; // The offset value is in char, and is related to the begining of string pool. ushort* pCount = m_pPool + iOffset; int count = (int)pCount[0]; // The number of strings in the array BCLDebug.Assert(count != 0, "[CultureTableRecord.GetStringArray]Expected non-zero length array"); String[] values = new String[count]; // Get past count and cast to uint uint* pStringArray = (uint*)(pCount + 1); // Get our strings for (int i = 0; i < count; i++) values[i] = GetString(pStringArray[i]); return (values); } // Get first string in this array of strings private unsafe String GetStringArrayDefault(uint iOffset) { if (iOffset == 0) return String.Empty; // The offset value is in char, and is related to the begining of string pool. ushort* pCount = m_pPool + iOffset; BCLDebug.Assert(pCount[0] != 0, "[CultureTableRecord.GetStringArrayDefault]Expected non-zero length array"); // Get past count and cast to uint uint* pStringArray = (uint*)(pCount + 1); // We had strings, return the first one return GetString(pStringArray[0]); } // Get the user override or the first array of this string array private String GetOverrideStringArrayDefault(uint iOffset, int iWindowsFlag) { #if !FEATURE_PAL if (this.UseGetLocaleInfo) { // Get User Override value if necessary String value = CultureInfo.nativeGetLocaleInfo(InteropLCID, iWindowsFlag); if (value != null && value.Length > 0) return value; } #endif // If override wasn't available, return the table version return GetStringArrayDefault(iOffset); } private ushort GetOverrideUSHORT(ushort iData, int iWindowsFlag) { #if !FEATURE_PAL if (this.UseGetLocaleInfo) { // Get User Override value if necessary String value = CultureInfo.nativeGetLocaleInfo(InteropLCID, iWindowsFlag); if (value != null && value.Length > 0) { // Now we need an integer for this string. Int16 intValue; if (Int16.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out intValue)) return unchecked((ushort)intValue); } } #endif return iData; } private unsafe int[] GetWordArray(uint iData) { if (iData == 0) return new int[0]; ushort* pWord = this.m_pPool + iData; int count = (int)pWord[0]; // The number of words in the array BCLDebug.Assert(count != 0, "[CultureTableRecord.GetWordArray]Expected non-zero length array"); int[] values = new int[count]; pWord++; // Get past count for (int i = 0; i < count; i++) { values[i] = pWord[i]; } return (values); } private int[] GetOverrideGrouping(uint iData, int iWindowsFlag) { #if !FEATURE_PAL if (this.UseGetLocaleInfo) { // Get User Override value if necessary String value = CultureInfo.nativeGetLocaleInfo(InteropLCID, iWindowsFlag); if (value != null && value.Length > 0) { // Got a grouping, need to convert it. int[] values = ConvertWin32GroupString(value); if (values != null) { // If non-null value are found, retured it. // Otherwise, fallback to default values. return (values); } } } #endif // No Override, use it from the tables. return GetWordArray(iData); } // The actual LCID, used when a name lookup leads to a custom sort (thus // 'de-DE-deudi' will be 0x10407 rather than the plain old 0x0407 of 'de-DE'). internal int ActualCultureID { get { if(0 == this.m_ActualCultureID) { this.m_ActualCultureID = this.ILANGUAGE; } return(this.m_ActualCultureID); } } // The actual name, used when an LCID lookup leads to a custom sort (thus will be // 0x10407 will be 'de-DE-deudi' rather than the plain old 'de-DE' of 0x0407). internal string ActualName { get { if(null == this.m_ActualName) { this.m_ActualName = this.SNAME; } return(this.m_ActualName); } } internal bool IsNeutralCulture { get { return ((IFLAGS & (ushort)CultureFlags.IsSpecificCulture) == 0); } } private bool IsOptionalCalendar(int calendarId) { for (int i=0; i iDigits);} } // (user can override) number of fractional digits internal unsafe ushort INEGNUMBER { get { return (this.m_pData->iNegativeNumber); } } // (user can override) negative number format internal unsafe ushort ICURRDIGITS { get { return (this.m_pData->iCurrencyDigits); } } // (user can override) # local monetary fractional digits internal unsafe ushort ICURRENCY { get { return (this.m_pData->iCurrency); } } // (user can override) positive currency format internal unsafe ushort INEGCURR { get { return (this.m_pData->iNegativeCurrency); } } // (user can override) negative currency format // internal unsafe ushort ILEADINGZEROS { get { return GetOverrideUSHORT(this.m_pData->iLEADINGZEROS, CultureTableData.LOCALE_ILEADINGZEROS); } } // (user can override) leading zeros 0 leading zeros, 1 ading zeros internal unsafe ushort ICALENDARTYPE { get { #if !FEATURE_PAL // Expand the code of GetOverrideUSHORT() since we don't want to create an int array (IOPTIONALCALENDARS) from every time that we call GetOverrideUSHORT(). if (this.m_bUseUserOverride) { // Get User Override value if necessary String value = CultureInfo.nativeGetLocaleInfo(ActualCultureID, CultureTableRecord.LOCALE_ICALENDARTYPE); if (value != null && value.Length > 0) { // Now we need an integer for this string. Int16 intValue; if (Int16.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out intValue) && IsOptionalCalendar((int)intValue)) return unchecked((ushort)intValue); } } #endif // !FEATURE_PAL return ((ushort)IOPTIONALCALENDARS[0]); } } internal unsafe ushort IFIRSTWEEKOFYEAR { get { return GetOverrideUSHORT(this.m_pData->iFirstWeekOfYear, CultureTableData.LOCALE_IFIRSTWEEKOFYEAR); } } // (user can override) first week of year // internal unsafe ushort I---- { get { return GetOverrideUSHORT(this.m_pData->i----, CultureTableData.LOCALE_I----); } } // (user can override) ---- code (RegionInfo) internal unsafe ushort IMEASURE { get { return GetOverrideUSHORT(this.m_pData->iMeasure, CultureTableData.LOCALE_IMEASURE); } } // (user can override) system of measurement 0ric, 1(RegionInfo) internal unsafe ushort IDIGITSUBSTITUTION { get { return GetOverrideUSHORT(this.m_pData->iDigitSubstitution, CultureTableData.LOCALE_IDIGITSUBSTITUTION); } } // (user can override) Digit substitution 0text, 1e/arabic, 2ive/national (2 seems to be unused) // Grouping internal unsafe int[] SGROUPING { get { return GetOverrideGrouping(this.m_pData->waGrouping, CultureTableData.LOCALE_SGROUPING); } } // (user can override) grouping of digits internal unsafe int[] SMONGROUPING { get { return GetOverrideGrouping(this.m_pData->waMonetaryGrouping, CultureTableData.LOCALE_SMONGROUPING); } } // (user can override) monetary grouping of digits // Strings internal unsafe String SLIST { get { return GetOverrideString(this.m_pData->sListSeparator, CultureTableData.LOCALE_SLIST); } } // (user can override) list Separator internal unsafe String SDECIMAL { get { return GetString(this.m_pData->sDecimalSeparator); } } // (user can override) decimal Separator internal unsafe String STHOUSAND { get { return GetString(this.m_pData->sThousandSeparator); } } // (user can override) thousands Separator internal unsafe String SCURRENCY { get { return GetString(this.m_pData->sCurrency); } } // (user can override) local monetary symbol internal unsafe String SMONDECIMALSEP { get { return GetString(this.m_pData->sMonetaryDecimal); } } // (user can override) monetary decimal Separator internal unsafe String SMONTHOUSANDSEP { get { return GetString(this.m_pData->sMonetaryThousand); } } // (user can override) monetary thousands separator internal unsafe String SNEGATIVESIGN { get { return GetString(this.m_pData->sNegativeSign); } } // (user can override) negative sign internal unsafe String S1159 { get { return GetString(this.m_pData->sAM1159); } } // (user can override) AM designator internal unsafe String S2359 { get { return GetString(this.m_pData->sPM2359); } } // (user can override) PM designator // String array DEFAULTS // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to. internal unsafe String STIMEFORMAT { get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saTimeFormat)); } } // (user can override) time format internal unsafe String SSHORTTIME { get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saShortTime)); } } // short time format internal unsafe String SSHORTDATE { get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saShortDate)); } } // (user can override) short date format internal unsafe String SLONGDATE { get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saLongDate)); } } // (user can override) long date format internal unsafe String SYEARMONTH { get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saYearMonth)); } } // (user can override) year/month format internal unsafe String SMONTHDAY { get { return ReescapeWin32String(GetString(this.m_pData->sMonthDay)); } } // month/day format (single string, no override) // String arrays internal unsafe String[] STIMEFORMATS { get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saTimeFormat)); } } // (user can override) time format internal unsafe String[] SSHORTTIMES { get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saShortTime)); } } // short time format internal unsafe String[] SSHORTDATES { get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saShortDate)); } } // (user can override default only) short date format internal unsafe String[] SLONGDATES { get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saLongDate)); } } // (user can override default only) long date format internal unsafe String[] SYEARMONTHS { get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saYearMonth)); } } // (user can override) date year/month format. (9x doesn't support override) internal unsafe String[] SNATIVEDIGITS { // (user can override) native characters for digits 0-9 get { #if !FEATURE_PAL string[] values; if (this.m_bUseUserOverride) { String value; // Get User Override value if it exists and convert it to a string array // If the user default is a custom culture for OS then skip this step. if((this.CultureID != CultureInfo.LOCALE_CUSTOM_DEFAULT) && null != (value = CultureInfo.nativeGetLocaleInfo(this.ActualCultureID, CultureTableData.LOCALE_SNATIVEDIGITS)) && (value.Length == 10)) { values = new string[10]; for(int i = 0; i < value.Length; i++) { values[i] = value[i].ToString(CultureInfo.InvariantCulture); } return(values); } } #endif // !FEATURE_PAL return GetStringArray(this.m_pData->saNativeDigits); } } // Integer ones are all pretty trivial internal unsafe ushort ILANGUAGE { get { return this.m_pData->iLanguage; } } // // internal unsafe ushort IDEFAULTLANGUAGE { get { return this.m_pData->iDEFAULTLANGUAGE; } } // Default language if this is a rare lcid (Windows Only) internal unsafe ushort IDEFAULTANSICODEPAGE { get { return this.m_pData->iDefaultAnsiCodePage; } } // default ansi code page ID (ACP) internal unsafe ushort IDEFAULTOEMCODEPAGE { get { return this.m_pData->iDefaultOemCodePage; } } // default oem code page ID (OCP or OEM) internal unsafe ushort IDEFAULTMACCODEPAGE { get { return this.m_pData->iDefaultMacCodePage; } } // default macintosh code page internal unsafe ushort IDEFAULTEBCDICCODEPAGE { get { return this.m_pData->iDefaultEbcdicCodePage; } } // default EBCDIC code page internal unsafe ushort IGEOID { get { return this.m_pData->iGeoId; } } // GeoId (RegionInfo) // internal unsafe ushort IPAPERSIZE { get { return this.m_pData->iPAPERSIZE; } } // default paper size (RegionInfo) // internal unsafe ushort IINTLCURRENCYDIGITS { get { return this.m_pData->iINTLCURRENCYDIGITS; } } // # of digits after decimal in intl currency format (Windows Only) internal unsafe ushort INEGATIVEPERCENT { get { return this.m_pData->iNegativePercent; } } // internal unsafe ushort IPOSITIVEPERCENT { get { return this.m_pData->iPositivePercent; } } // internal unsafe ushort IPARENT { get { return this.m_pData->iParent; } } // internal unsafe ushort ILINEORIENTATIONS { get { return this.m_pData->iLineOrientations; } } // internal unsafe uint ICOMPAREINFO { get { return this.m_pData->iCompareInfo; } } // internal unsafe uint IFLAGS { get { return this.m_pData->iFlags; } } // Flags for culture // OptionalCalendars internal unsafe int[] IOPTIONALCALENDARS { get { return GetWordArray(this.m_pData->waCalendars); } } // additional calendar type(s), semicolon seperated, ie: '1;6' // Strings internal unsafe String SNAME { get { return GetString(this.m_pData->sName); } } // internal unsafe String SABBREVLANGNAME { get { return GetString(this.m_pData->sAbbrevLang); } } // abbreviated language name internal unsafe String SISO639LANGNAME { get { return GetString(this.m_pData->sISO639Language); } } // // internal unsafe String SENGLISHLANGUAGE { get { return GetString(this.m_pData->sENGLISHLANGUAGE); } } // English name for this language (Windows Only) // internal unsafe String SNATIVELANGUAGE { get { return GetString(this.m_pData->sNATIVELANGUAGE); } } // Native name of this language (Windows Only) internal unsafe String SENG---- { get { return GetString(this.m_pData->sEnglish----); } } // english ---- name (RegionInfo) internal unsafe String SNATIVE---- { get { return GetString(this.m_pData->sNative----); } } // native ---- name (RegionInfo) internal unsafe String SABBREVCTRYNAME { get { return GetString(this.m_pData->sAbbrev----); } } // abbreviated ---- name (RegionInfo) internal unsafe String SISO3166CTRYNAME { get { return GetString(this.m_pData->sISO3166----Name); } } // (RegionInfo) internal unsafe String SINTLSYMBOL { get { return GetString(this.m_pData->sIntlMonetarySymbol); } } // international monetary symbol (RegionInfo) internal unsafe String SENGLISHCURRENCY { get { return GetString(this.m_pData->sEnglishCurrency); } } // English name for this currency (RegionInfo) internal unsafe String SNATIVECURRENCY { get { return GetString(this.m_pData->sNativeCurrency); } } // Native name for this currency (RegionInfo) internal unsafe String SENGDISPLAYNAME { get { return GetString(this.m_pData->sEnglishDisplayName); } } // internal unsafe String SISO639LANGNAME2 { get { return GetString(this.m_pData->sISO639Language2); } } // internal unsafe String SNATIVEDISPLAYNAME { get { // Special case for ----. if (CultureInfo.GetLangID(ActualCultureID) == 0x0404 && CultureInfo.GetLangID(CultureInfo.InstalledUICulture.LCID) == 0x0404 && !IsCustomCulture) { return (CultureInfo.nativeGetLocaleInfo(0x0404, LOCALE_SNATIVELANGNAME) + " (" + CultureInfo.nativeGetLocaleInfo(0x0404, LOCALE_SNATIVECTRYNAME) + ")"); } return GetString(this.m_pData->sNativeDisplayName); } } // internal unsafe String SPERCENT { get { return GetString(this.m_pData->sPercent); } } // internal unsafe String SNAN { get { return GetString(this.m_pData->sNaN); } } // internal unsafe String SPOSINFINITY { get { return GetString(this.m_pData->sPositiveInfinity); } } // internal unsafe String SNEGINFINITY { get { return GetString(this.m_pData->sNegativeInfinity); } } // internal unsafe String SADERA { get { return GetString(this.m_pData->sAdEra); } } // localized names for the A.D. Era internal unsafe String SABBREVADERA { get { return GetString(this.m_pData->sAbbrevAdEra); } } // abbreviated localized names for the A.D. Era internal unsafe String SISO3166CTRYNAME2 { get { return GetString(this.m_pData->sISO3166----Name2); } } // (RegionInfo) internal unsafe String SREGIONNAME { get { return GetString(this.m_pData->sRegionName); } } // (RegionInfo) internal unsafe String SPARENT { get { return GetString(this.m_pData->sParent); } } // internal unsafe String SCONSOLEFALLBACKNAME { get { return GetString(this.m_pData->sConsoleFallbackName); } } internal unsafe String SSPECIFICCULTURE { get { return GetString(this.m_pData->sSpecificCulture); } } // String Arrays internal unsafe String[] SDAYNAMES { get { return GetStringArray(this.m_pData->saDayNames); } } // day names internal unsafe String[] SABBREVDAYNAMES { get { return GetStringArray(this.m_pData->saAbbrevDayNames); } } // abbreviated day names internal unsafe String[] SSUPERSHORTDAYNAMES { get { return GetStringArray(this.m_pData->saSuperShortDayNames); } } // one letter day names internal unsafe String[] SMONTHNAMES { get { return GetStringArray(this.m_pData->saMonthNames); } } // month names internal unsafe String[] SABBREVMONTHNAMES { get { return GetStringArray(this.m_pData->saAbbrevMonthNames); } } // abbreviated month names internal unsafe String[] SMONTHGENITIVENAMES { get { return GetStringArray(this.m_pData->saMonthGenitiveNames); } } // internal unsafe String[] SABBREVMONTHGENITIVENAMES{ get { return GetStringArray(this.m_pData->saAbbrevMonthGenitiveNames); } }// internal unsafe String[] SNATIVECALNAMES { get { return GetStringArray(this.m_pData->saNativeCalendarNames); } } // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number internal unsafe String[] SDATEWORDS { get { return GetStringArray(this.m_pData->saDateWords); } } // internal unsafe String[] SALTSORTID { get { return GetStringArray(this.m_pData->saAltSortID); } } // The array of alternate sort names // Fontsignature // internal unsafe ushort FONTSIGNATURE { get { return this.m_pData->waFONTSIGNATURE; } } // Font signature (16 WORDS) (Windows Only) // DateTimeFormatFlags internal unsafe DateTimeFormatFlags IFORMATFLAGS{ get { return (DateTimeFormatFlags)this.m_pData->iFormatFlags; } } // // Special handling required for these fields // (user can override) positive sign. We use "+" if empty (windows data is usually empty) internal unsafe String SPOSITIVESIGN { get { String strTemp = GetString(this.m_pData->sPositiveSign); if (strTemp == null || strTemp.Length == 0) strTemp = "+"; return strTemp; } } internal static bool IsCustomCultureId(int cultureId) { if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED) return true; return false; } private unsafe ushort ConvertFirstDayOfWeekMonToSun(int iTemp) { // Convert Mon-Sun to Sun-Sat format if (iTemp < 0 || iTemp > 6) { // If invalid data exist in registry, assume // the first day of week is Monday. iTemp = 1; } else { if (iTemp == 6) { iTemp = 0; } else { iTemp++; } } return unchecked((ushort)iTemp); } // (user can override) first day of week (0 is Sunday) internal unsafe ushort IFIRSTDAYOFWEEK { get { return this.m_pData->iFirstDayOfWeek; } } internal unsafe ushort IINPUTLANGUAGEHANDLE { get { // Remember this returns SPANISH_INTERNATIONAL_SORT even // in the deprecated case. return (this.m_pData->iInputLanguageHandle); } } internal unsafe ushort ITEXTINFO { get { ushort textInfo = this.m_pData->iTextInfo; // Need to return SPANISH_TRADITIONAL_SORT even if we're faking it // (Hack because SPANISH_TRADITIONAL_SORT isn't in the table) if (this.CultureID == (ushort)SPANISH_TRADITIONAL_SORT) textInfo = (ushort)SPANISH_TRADITIONAL_SORT; // Make sure custom culture and unknown get something if (textInfo == CultureInfo.LOCALE_CUSTOM_DEFAULT || textInfo == 0) textInfo = CultureInfo.LOCALE_INVARIANT; return textInfo; } } #if !FEATURE_PAL // If we get a group from the registry, then its in 3;0 format with the 0 backwards // of how NLS uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa) static private int[] ConvertWin32GroupString(String win32Str) { // None of these cases make any sense if (win32Str == null || win32Str.Length == 0 || win32Str[0] == '0') { return (new int[] {3}); } // Since its in n;n;n;n;n format, we can always get the length quickly int[] values; if (win32Str[win32Str.Length - 1] == '0') { // Trailing 0 gets dropped. 1;0 -> 1 values = new int[(win32Str.Length / 2)]; } else { // Need extra space for trailing zero 1 -> 1;0 values = new int[(win32Str.Length / 2) + 2]; values[values.Length - 1] = 0; } int i; int j; for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++) { // Note that this # shouldn't ever be zero, 'cause 0 is only at end // But we'll test because its registry that could be anything if (win32Str[i] < '1' || win32Str[i] > '9') return new int[] {3}; values[j] = (int)(win32Str[i] - '0'); } return (values); } #endif //////////////////////////////////////////////////////////////////////////// // // Unescape a Win32 style quote string // // This is also the escaping style used by custom culture data files // // This removes the 'fred' and 'fred''s' windows quoted formatting from a string. // The output string will NOT have ANY escaping. Currently its used for // separators, where the output string has no characters with special meaning // // We don't build the stringbuilder unless we find a '. If we find a ', we // always build a stringbuilder because we need to remove the '. // //////////////////////////////////////////////////////////////////////////// static private String UnescapeWin32String(String str, int start, int end) { StringBuilder result = null; bool inQuote = false; for (int i = start; i < str.Length && i <= end; i++) { // Look for quote if (str[i] == '\'') { // Already in quote? if (inQuote) { BCLDebug.Assert(result != null, "[CultureTable.UnescapeWin32String]Expect result to be non-null"); // See another single quote. Is this '' of 'fred''s' or ending quote? if (i + 1 < str.Length) { if (str[i+1] == '\'') { // Append a ' and keep going (so we don't turn off quote mode) result.Append('\''); i++; continue; } } inQuote = false; } else { // Found beginning quote, remove it. inQuote = true; if (result == null) result = new StringBuilder(str, start, i - start, str.Length); } } else { // If we have a builder we need to add our non-quote char if (result != null) result.Append(str[i]); } } // No ', just return input string substring if (result == null) return (str.Substring(start, end-start + 1)); // Had ', need to use the builder return (result.ToString()); } //////////////////////////////////////////////////////////////////////////// // // Reescape a Win32 style quote string as a NLS+ style quoted string // // This is also the escaping style used by custom culture data files // // NLS+ uses \ to escape the next character, whether in a quoted string or // not, so we always have to change \ to \\. // // NLS+ uses \' to escape a quote inside a quoted string so we have to change // '' to \' (if inside a quoted string) // // We don't build the stringbuilder unless we find something to change //////////////////////////////////////////////////////////////////////////// static private String ReescapeWin32String(String str) { // If we don't have data, then don't try anything if (str == null) return null; StringBuilder result = null; bool inQuote = false; for (int i = 0; i < str.Length; i++) { // Look for quote if (str[i] == '\'') { // Already in quote? if (inQuote) { // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote? if (i + 1 < str.Length && str[i+1] == '\'') { // Found another ', so we have ''. Need to add \' instead. // 1st make sure we have our stringbuilder if (result == null) result = new StringBuilder(str, 0, i, str.Length * 2); // Append a \' and keep going (so we don't turn off quote mode) result.Append("\\'"); i++; continue; } // Turning off quote mode, fall through to add it inQuote = false; } else { // Found beginning quote, fall through to add it inQuote = true; } } // Is there a single \ character? else if (str[i] == '\\') { // Found a \, need to change it to \\ // 1st make sure we have our stringbuilder if (result == null) result = new StringBuilder(str, 0, i, str.Length * 2); // Append our \\ to the string & continue result.Append("\\\\"); continue; } // If we have a builder we need to add our character if (result != null) result.Append(str[i]); } // Unchanged string? , just return input string if (result == null) return str; // String changed, need to use the builder return result.ToString(); } static private String[] ReescapeWin32Strings(String[] array) { if (array != null) { for (int i = 0; i < array.Length; i++) { array[i] = ReescapeWin32String(array[i]); } } return array; } internal unsafe String STIME { get { // Compute SDATE from STIMEFORMAT String timeFormat = GetOverrideStringArrayDefault(this.m_pData->saTimeFormat, CultureTableData.LOCALE_STIMEFORMAT); return GetTimeSeparator(timeFormat); } } internal unsafe String SDATE { get { // Compute SDATE from SSHORTDATE String shortDate = GetOverrideStringArrayDefault(this.m_pData->saShortDate, CultureTableData.LOCALE_SSHORTDATE); return GetDateSeparator(shortDate); } } static private String GetTimeSeparator(String format) { // Time format separator (ie: : in 12:39:00) // // We calculate this from the provided time format // // // Find the time separator so that we can pretend we know STIME. // String strUse = String.Empty; int count = 0; int separatorStart = -1; // Look through the whole string for (count = 0; count < format.Length; count++) { // See if we have Hhms if (format[count] == 'H' || format[count] == 'h' || format[count] == 'm' || format[count] == 's') { // Found a time part, find out when it changes char cFound = format[count]; for (count++; count < format.Length && format[count] == cFound; count++) { // Done } // Did we find anything? if (count < format.Length) { // We found start of separator separatorStart = count; } // In either case we changed hms types, so we found one or we didn't, but we need to stop break; } // If it was quotes, ignore quoted stuff if (format[count] == '\'') { // // Ignore quotes. // for (count++; count < format.Length && (format[count] != '\''); count++) { // Done } // Don't go past end of string } // Advance to next char (skipping unknown char or last quote) } // Now we need to find the end of the separator if (separatorStart != -1) { for (count = separatorStart; count < format.Length; count++) { // See if we have Hhms if (format[count] == 'H' || format[count] == 'h' || format[count] == 'm' || format[count] == 's') { // Found a time part, stop, we can look for our separator // From [separatorStart, count) is our string, except we don't want ''s strUse = UnescapeWin32String(format, separatorStart, count - 1); break; } // If it was quotes, ignore quoted stuff if (format[count] == '\'') { // // Ignore quotes. // for (count++; count < format.Length && (format[count] != '\''); count++) { // Done } // Don't go past end of string } // Advance to next char (skipping unknown char or last quote) } } // Return the one we're using return strUse; } static private String GetDateSeparator(String format) { // Date format separator (ie: / in 9/1/03) // // We calculate this from the provided short date // // // Find the date separator so that we can pretend we know SDATE. // String strUse = String.Empty; int count = 0; int separatorStart = -1; // Look through the whole string for (count = 0; count < format.Length; count++) { // See if we have dyM if (format[count] == 'd' || format[count] == 'y' || format[count] == 'M') { // Found a time part, find out when it changes char cFound = format[count]; for (count++; count < format.Length && format[count] == cFound; count++) { // Done } // Did we find anything? if (count < format.Length) { // We found start of separator separatorStart = count; } // In either case we changed dyM types, so we found one or we didn't, but we need to stop break; } // If it was quotes, ignore quoted stuff if (format[count] == '\'') { // // Ignore quotes. // for (count++; count < format.Length && (format[count] != '\''); count++) { // Done } // Don't go past end of string } // Advance to next char (skipping unknown char or last quote) } // Now we need to find the end of the separator if (separatorStart != -1) { for (count = separatorStart; count < format.Length; count++) { // See if we have yMd if (format[count] == 'y' || format[count] == 'M' || format[count] == 'd') { // Found a time part, stop, we can look for our separator // From [separatorStart, count) is our string, except we don't want ''s strUse = UnescapeWin32String(format, separatorStart, count - 1); break; } // If it was quotes, ignore quoted stuff if (format[count] == '\'') { // // Ignore quotes. // for (count++; count < format.Length && (format[count] != '\''); count++) { // Done } // Don't go past end of string } // Advance to next char (skipping unknown char or last quote) } } // Return the one we're using return strUse; } //////////////////////////////////////////////////////////////////////////// // // Parameters: // calendarValueOnly Retrieve the values which are affected by the calendar change of DTFI. // This will cause values like longTimePattern not be retrieved since it is // not affected by the Calendar property in DTFI. // //////////////////////////////////////////////////////////////////////////// internal unsafe void GetDTFIOverrideValues(ref DTFIUserOverrideValues values) { BCLDebug.Assert(UseUserOverride, "CultureTableRecord.GetDTFIOverrideValues(): Call this only when UseUserOverride is true."); bool result = false; if (UseGetLocaleInfo) result = CultureInfo.nativeGetDTFIUserValues(InteropLCID, ref values); if (result) { // if we got values.yearMonthPattern = null this means the data is not located in the registry and // we couldn't call GetLocaleInfo. we leave yearMonthPattern as null here so the caller (DTFI) // will initialize it properly. values.firstDayOfWeek = ConvertFirstDayOfWeekMonToSun((int)values.firstDayOfWeek); // Need to do escaping of win32/file type patterns to NLS type patterns values.shortDatePattern = ReescapeWin32String(values.shortDatePattern); values.longDatePattern = ReescapeWin32String(values.longDatePattern); values.longTimePattern = ReescapeWin32String(values.longTimePattern); values.yearMonthPattern = ReescapeWin32String(values.yearMonthPattern); } else { // // We do not use user-override values or something failed during the call to GetLocaleInfo(). Use the information in culture.nlp. // values.firstDayOfWeek = IFIRSTDAYOFWEEK; values.calendarWeekRule = IFIRSTWEEKOFYEAR; values.shortDatePattern = SSHORTDATE; values.longDatePattern = SLONGDATE; values.yearMonthPattern = SYEARMONTH; values.amDesignator = S1159; values.pmDesignator = S2359; values.longTimePattern = STIMEFORMAT; } } internal unsafe void GetNFIOverrideValues(NumberFormatInfo nfi) { bool result = false; if (UseGetLocaleInfo) { result = CultureInfo.nativeGetNFIUserValues(InteropLCID, nfi); } if (!result) { // Something failed during the call to GetLocaleInfo(). Use the information in culture.nlp. nfi.numberDecimalDigits = IDIGITS; nfi.numberNegativePattern = INEGNUMBER; nfi.currencyDecimalDigits = ICURRDIGITS; nfi.currencyPositivePattern = ICURRENCY; nfi.currencyNegativePattern = INEGCURR; nfi.negativeSign = SNEGATIVESIGN; nfi.numberDecimalSeparator = SDECIMAL; nfi.numberGroupSeparator = STHOUSAND; nfi.positiveSign = SPOSITIVESIGN; nfi.currencyDecimalSeparator= SMONDECIMALSEP; nfi.currencySymbol = SCURRENCY; nfi.currencyGroupSeparator = SMONTHOUSANDSEP; nfi.nativeDigits = SNATIVEDIGITS; nfi.digitSubstitution = IDIGITSUBSTITUTION; } else if(-1 == nfi.digitSubstitution) { // This is a Win2000 and above property, so when it is marked as -1 // (an invalid value) we know it failed for Win9x reasons and that // we should fall back to getting this infotmation from culture.nlp. nfi.digitSubstitution = IDIGITSUBSTITUTION; } nfi.numberGroupSizes = SGROUPING; nfi.currencyGroupSizes = SMONGROUPING; nfi.percentDecimalDigits = nfi.numberDecimalDigits; nfi.percentDecimalSeparator = nfi.numberDecimalSeparator; nfi.percentGroupSizes = nfi.numberGroupSizes; nfi.percentGroupSeparator = nfi.numberGroupSeparator; nfi.percentNegativePattern = INEGATIVEPERCENT; nfi.percentPositivePattern = IPOSITIVEPERCENT; nfi.percentSymbol = SPERCENT; if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+"; //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the //decimal point doesn't show up. We'll just hack this here because our default currency format will never use nfi. if (nfi.currencyDecimalSeparator.Length==0) { nfi.currencyDecimalSeparator= SMONDECIMALSEP; } } // EverettDataItem // // Everett can't deserialize using names/ids, so it has to use the data item. internal unsafe int EverettDataItem() { // See if its a custom culture if (this.IsCustomCulture) { // They're hosed, this is a custom culture, return 0 (Invariant) // It'd be better if Everett threw an error, but the accessors don't have to do // range checking on this, so we'd just read off the end of the data and get // junk, which wouldn't be guaranteed to throw an error. Invariant is a better choice. return 0; } InitEverettCultureDataItemMapping(); // Normal culture, look up its data item from our LCID // Do a binary search int left = 0; int right = (m_EverettCultureDataItemMappingsSize/2) - 1; while (left <= right) { int mid = (left+right)/2; int result = this.m_CultureID - m_EverettCultureDataItemMappings[mid*2]; if (result == 0) { // Found it, return the index return m_EverettCultureDataItemMappings[mid*2 + 1]; } if (result < 0) right = mid - 1; else left = mid + 1; } // They're hosed, couldn't find an Everett data item for this culture. // It'd be better if Everett threw an error, but the accessors don't have to do // range checking on this, so we'd just read off the end of the data and get // junk, which wouldn't be guaranteed to throw an error. Invariant is a better choice. return 0; } internal unsafe int EverettRegionDataItem() { // See if its a custom culture if (this.IsCustomCulture) { // They're hosed, this is a custom culture, return 0 (Invariant) // It'd be better if Everett threw an error, but the accessors don't have to do // range checking on this, so we'd just read off the end of the data and get // junk, which wouldn't be guaranteed to throw an error. Invariant is a better choice. return 0; } InitEverettRegionDataItemMapping(); // Normal culture, look up its data item from our LCID // Do a binary search int left = 0; int right = (m_EverettRegionDataItemMappingsSize/2) - 1; while (left <= right) { int mid = (left+right)/2; int result = this.m_CultureID - m_EverettRegionDataItemMappings[mid*2]; if (result == 0) { // Found it, return the index return m_EverettRegionDataItemMappings[mid*2 + 1]; } if (result < 0) right = mid - 1; else left = mid + 1; } // They're hosed, couldn't find an Everett data item for this culture. // It'd be better if Everett threw an error, but the accessors don't have to do // range checking on this, so we'd just read off the end of the data and get // junk, which wouldn't be guaranteed to throw an error. Invariant is a better choice. return 0; } internal static unsafe int IdFromEverettDataItem(int iDataItem) { InitEverettDataItemToLCIDMappings(); // Assert that it exists BCLDebug.Assert(iDataItem >= 0 && iDataItem < m_EverettDataItemToLCIDMappingsSize, String.Format( CultureInfo.CurrentCulture, "[CultureTableRecord.IdFromEverettDataItem]Expected Everett data item in range of data table {0}", iDataItem)); if (iDataItem < 0 || iDataItem >= m_EverettDataItemToLCIDMappingsSize) { // If the dataItem is not valid, throw. throw new SerializationException(Environment.GetResourceString("Serialization_InvalidFieldState")); } return m_EverettDataItemToLCIDMappings[iDataItem]; } internal static unsafe int IdFromEverettRegionInfoDataItem(int iDataItem) { InitEverettRegionDataItemToLCIDMappings(); // Assert that it exists BCLDebug.Assert(iDataItem >= 0 && iDataItem < m_EverettRegionInfoDataItemToLCIDMappingsSize, String.Format( CultureInfo.CurrentCulture, "[CultureTableRecord.IdFromEverettRegionInfoDataItem]Expected Everett data item in range of data table {0}", iDataItem)); if (iDataItem < 0 || iDataItem >= m_EverettRegionInfoDataItemToLCIDMappingsSize) { // If the dataItem is not valid, throw. throw new SerializationException(Environment.GetResourceString("Serialization_InvalidFieldState")); } return m_EverettRegionInfoDataItemToLCIDMappings[iDataItem]; } // The const here should be in sync with the one defined in the native side. const int INT32TABLE_EVERETT_REGION_DATA_ITEM_MAPPINGS = 0; const int INT32TABLE_EVERETT_CULTURE_DATA_ITEM_MAPPINGS = 1; const int INT32TABLE_EVERETT_DATA_ITEM_TO_LCID_MAPPINGS = 2; const int INT32TABLE_EVERETT_REGION_DATA_ITEM_TO_LCID_MAPPINGS = 3; // Call InitEverettRegionDataItemMapping() before using these two. static unsafe int* m_EverettRegionDataItemMappings = null; static unsafe int m_EverettRegionDataItemMappingsSize = 0; //////////////////////////////////////////////////////////////////////// // // Initialize the data used for mapping RegionInfo ID to dataItem. // Everett uses dataItem in persisting RegionInfo. // //////////////////////////////////////////////////////////////////////// private static unsafe void InitEverettRegionDataItemMapping() { if (m_EverettRegionDataItemMappings == null) { int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_REGION_DATA_ITEM_MAPPINGS, out m_EverettRegionDataItemMappingsSize); m_EverettRegionDataItemMappings = temp; BCLDebug.Assert(m_EverettRegionDataItemMappings != null, "CultureTableRecord.m_EverettRegionDataItemMappings can not be null"); BCLDebug.Assert(m_EverettRegionDataItemMappingsSize > 0, "CultureTableRecord.m_EverettRegionDataItemMappingsSize > 0"); } } // Call InitEverettCultureDataItemMapping before using these two. unsafe static int* m_EverettCultureDataItemMappings = null; static int m_EverettCultureDataItemMappingsSize = 0; //////////////////////////////////////////////////////////////////////// // // Initialize the data used for mapping CultureInfo ID to dataItem. // Everett uses dataItem in persisting CultureInfo. // //////////////////////////////////////////////////////////////////////// private static unsafe void InitEverettCultureDataItemMapping() { if (m_EverettCultureDataItemMappings == null) { int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_CULTURE_DATA_ITEM_MAPPINGS, out m_EverettCultureDataItemMappingsSize); m_EverettCultureDataItemMappings = temp; BCLDebug.Assert(m_EverettCultureDataItemMappings != null, "CultureTableRecord.m_EverettCultureDataItemMappings can not be null"); BCLDebug.Assert(m_EverettCultureDataItemMappingsSize > 0, "CultureTableRecord.m_EverettCultureDataItemMappingsSize > 0"); } } // Call InitEverettDataItemToLCIDMappings() before using these two. private static unsafe int* m_EverettDataItemToLCIDMappings = null; private static int m_EverettDataItemToLCIDMappingsSize = 0; //////////////////////////////////////////////////////////////////////// // // Initialize the CultureInfo data used for mapping an Everett dataItem to a LCID. // Everett uses dataItem in persisting CultureInfo. // //////////////////////////////////////////////////////////////////////// private static unsafe void InitEverettDataItemToLCIDMappings() { if (m_EverettDataItemToLCIDMappings == null) { int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_DATA_ITEM_TO_LCID_MAPPINGS, out m_EverettDataItemToLCIDMappingsSize); m_EverettDataItemToLCIDMappings = temp; BCLDebug.Assert(m_EverettDataItemToLCIDMappings != null, "CultureTableRecord.m_EverettDataItemToLCIDMappings can not be null"); BCLDebug.Assert(m_EverettDataItemToLCIDMappingsSize > 0, "CultureTableRecord.m_EverettDataItemToLCIDMappingsSize > 0"); } } // Call InitEverettRegionDataItemToLCIDMappings() before using these two. private static unsafe int* m_EverettRegionInfoDataItemToLCIDMappings = null; private static int m_EverettRegionInfoDataItemToLCIDMappingsSize = 0; //////////////////////////////////////////////////////////////////////// // // Initialize the RegionInfo data used for mapping an Everett dataItem to a LCID. // Everett uses dataItem in persisting RegionInfo. // //////////////////////////////////////////////////////////////////////// private static unsafe void InitEverettRegionDataItemToLCIDMappings() { if (m_EverettRegionInfoDataItemToLCIDMappings == null) { int* temp = CultureInfo.nativeGetStaticInt32DataTable( INT32TABLE_EVERETT_REGION_DATA_ITEM_TO_LCID_MAPPINGS, out m_EverettRegionInfoDataItemToLCIDMappingsSize); m_EverettRegionInfoDataItemToLCIDMappings = temp; BCLDebug.Assert(m_EverettRegionInfoDataItemToLCIDMappings != null, "CultureTableRecord.m_EverettRegionInfoDataItemToLCIDMappings can not be null"); BCLDebug.Assert(m_EverettRegionInfoDataItemToLCIDMappingsSize > 0, "CultureTableRecord.m_EverettRegionInfoDataItemToLCIDMappingsSize > 0"); } } } //////////////////////////////////////////////////////////////////////////// // // This structure contains DateTimeFormatInfo properties that can be overridden by users. // We define this structure so that we can fill these values in one FCALL, instead of calling GetLocaleInfo() multiple times in // separate FCalls. // // [StructLayout(LayoutKind.Sequential, Pack=2)] internal struct DTFIUserOverrideValues { // DTFI values that are affected by calendar setttings. internal String shortDatePattern; internal String longDatePattern; internal String yearMonthPattern; // DTFI values that will not be affected by calendar settings. internal String amDesignator; internal String pmDesignator; internal String longTimePattern; internal int firstDayOfWeek; internal int padding1; // Add padding to make sure that we are aligned in DWORD. This is important for 64-bit platforms internal int calendarWeekRule; internal int padding2; // Add padding to make sure that we are aligned in DWORD. This is important for 64-bit platforms } // CultureData has a cloned strucure in the native side. we send this struct to the native side to be filled // by the native APIs (mostly GetLocaleInfo) to load the synthetic cultures data. // // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT // any change in this structure require a change in the cloned one in the native side. (ComNlsInfo.h/.cpp) // // Also we use the default alignment which is 8-bytes in the managed and native sides so don't use the "Pack" property here // // [StructLayout(LayoutKind.Sequential)] internal struct CultureData { internal string sIso639Language; // LOCALE_SISO639LANGNAME (TwoLetterISOLanguageName) internal string sIso3166----Name; // LOCALE_SISO3166CTRYNAME (TwoLetterISORegionName) internal string sListSeparator; // LOCALE_SLIST (ListSeparator) internal string sDecimalSeparator; // LOCALE_SDECIMAL (NumberDecimalSeparator) internal string sThousandSeparator; // LOCALE_STHOUSAND (NumberGroupSeparator) internal string sCurrency; // LOCALE_SCURRENCY (CurrencySymbol) internal string sMonetaryDecimal; // LOCALE_SMONDECIMALSEP (CurrencyDecimalSeparator) internal string sMonetaryThousand; // LOCALE_SMONTHOUSANDSEP (CurrencyGroupSeparator) internal string sNegativeSign; // LOCALE_SNEGATIVESIGN (NegativeSign) internal string sAM1159; // LOCALE_S1159 (AMDesignator) internal string sPM2359; // LOCALE_S2359 (PMDesignator) internal string sAbbrevLang; // LOCALE_SABBREVLANGNAME (ThreeLetterWindowsLanguageName) internal string sEnglishLanguage; // LOCALE_SENGLANGUAGE (Part of EnglishName) internal string sEnglish----; // LOCALE_SENG---- (Part of EnglishName) internal string sNativeLanguage; // LOCALE_SNATIVELANGNAME (Part of NativeName) internal string sNative----; // LOCALE_SNATIVECTRYNAME (Part of NativeName) internal string sAbbrev----; // LOCALE_SABBREVCTRYNAME (ThreeLetterWindowsRegionName) internal string sIntlMonetarySymbol; // LOCALE_SINTLSYMBOL (ISOCurrencySymbol) internal string sEnglishCurrency; // LOCALE_SENGCURRNAME (CurrencyEnglishName) internal string sNativeCurrency; // LOCALE_SNATIVECURRNAME (CurrencyNativeName) internal string saAltSortID; // LOCALE_SSORTNAME (SortName) internal string sParentName; // LOCALE_SPARENT (Parent) internal string sConsoleFallbackName; // LOCALE_SCONSOLEFALLBACKNAME (GetConsoleFallbackUICulture) // sPositiveSign in NLS always return empty string internal string sPositiveSign; // LOCALE_SPOSITIVESIGN (PositiveSign) // saNativeDigits should be converted to array of string instead of array of characters later. internal string saNativeDigits; // LOCALE_SNATIVEDIGITS (NativeDigits) internal string waGrouping; // LOCALE_SGROUPING (NumberGroupSizes) internal string waMonetaryGrouping; // LOCALE_SMONGROUPING (CurrencyGroupSizes) internal string waFontSignature; // LOCALE_FONTSIGNATURE (No API for it) // Some fields defined only post XP internal string sNaN; // LOCALE_SNAN (NaNSymbol) internal string sPositiveInfinity; // LOCALE_SPOSINFINITY (PositiveInfinitySymbol) internal string sNegativeInfinity; // LOCALE_SNEGINFINITY (NegativeInfinitySymbol) internal string sISO3166----Name2; // LOCALE_SISO3166CTRYNAME2 (ThreeLetterISORegionName) internal string sISO639Language2; // LOCALE_SISO639LANGNAME2 (ThreeLetterISOLanguageName) internal string [] saSuperShortDayNames; // LOCALE_SSHORTESTDAYNAME1..LOCALE_SSHORTESTDAYNAME7 (ShortestDayNames) // End of the fields defined only post XP internal string [] saTimeFormat; // EnumTimeFormats (GetAllDateTimePatterns('T')) internal string [] saShortDate; // EnumDateFormatsEx (GetAllDateTimePatterns('d')) internal string [] saLongDate; // EnumDateFormatsEx (GetAllDateTimePatterns('D')) internal string [] saYearMonth; // EnumDateFormatsEx (GetAllDateTimePatterns("Y")) internal string [] saMonthNames; // LOCALE_SMONTHNAME(1~13) (MonthNames) // LOCALE_SDAYNAME1 means Monday in NLS (need conversion in NLS+ internal string [] saDayNames; // LOCALE_SDAYNAME(1~7) (GetDayOfWeekNames) // LOCALE_SABBREVDAYNAME means Monday in NLS (need conversion in NLS+ internal string [] saAbbrevDayNames; // LOCALE_SABBREVDAYNAME(1~7) (GetAbbreviatedDayOfWeekNames/SuperShortDayNames) internal string [] saAbbrevMonthNames; // LOCALE_SABBREVMONTHNAME(1~13)(AbbreviatedMonthNames) internal string [] saNativeCalendarNames; // GetCalendarInfo/CAL_SCALNAME (NativeCalendarName) internal string [] saGenitiveMonthNames; // GetDateFormat with "dd MMMM" (MonthGenitiveNames) internal string [] saAbbrevGenitiveMonthNames; // GetDateFormat with "d MMM" (AbbreviatedMonthGenitiveNames) // use also EnumCalendarInfo/CAL_ICALINTVALUE internal ushort [] waCalendars; // LOCALE_IOPTIONALCALENDAR (OptionalCalendars) // iFirstDayOfWeek (0 is Monday for NLS and is Sunday in NLS+) internal int iFirstDayOfWeek; // LOCALE_IFIRSTDAYOFWEEK (FirstDayOfWeek) internal int iDigits; // LOCALE_IDIGITS (NumberDecimalDigits) internal int iNegativeNumber; // LOCALE_INEGNUMBER (NumberNegativePattern) internal int iCurrencyDigits; // LOCALE_ICURRDIGITS (CurrencyDecimalDigits) internal int iCurrency; // LOCALE_ICURRENCY (CurrencyPositivePattern) internal int iNegativeCurrency; // LOCALE_INEGCURR (CurrencyNegativePattern) internal int iFirstWeekOfYear; // LOCALE_IFIRSTWEEKOFYEAR (CalendarWeekRule) internal int iMeasure; // LOCALE_IMEASURE (IsMetric) internal int iDigitSubstitution; // LOCALE_IDIGITSUBSTITUTION (DigitSubstitution) internal int iDefaultAnsiCodePage; // LOCALE_IDEFAULTANSICODEPAGE (ANSICodePage) internal int iDefaultOemCodePage; // LOCALE_IDEFAULTCODEPAGE (OEMCodePage) internal int iDefaultMacCodePage; // LOCALE_IDEFAULTMACCODEPAGE (MacCodePage) internal int iDefaultEbcdicCodePage; // LOCALE_IDEFAULTEBCDICCODEPAGE(EBCDICCodePage) internal int iLocale; // LOCALE_I---- (No API for this field) internal int iPaperSize; // LOCALE_IPAPERSIZE (No API for this field) internal int iLeadingZeros; // LOCALE_IDAYLZERO (No API for this field) internal int iIntlCurrencyDigits; // LOCALE_IINTLCURRDIGITS (No API for this field) internal int iGeoId; // EnumSystemGeoID/GetGeoInfo (RegionInfo.GeoId) internal int iDefaultCalender; // LOCALE_ICALENDARTYPE (No API for this field) } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- UserMapPath.cs
- TextBox.cs
- DBSchemaRow.cs
- IOException.cs
- AbstractSvcMapFileLoader.cs
- BitmapEffectCollection.cs
- XmlTextReaderImplHelpers.cs
- SecuritySessionSecurityTokenProvider.cs
- AssemblyInfo.cs
- ICspAsymmetricAlgorithm.cs
- TextTrailingWordEllipsis.cs
- ClipboardProcessor.cs
- KerberosTokenFactoryCredential.cs
- ISCIIEncoding.cs
- _UriTypeConverter.cs
- CodePageEncoding.cs
- CanonicalFormWriter.cs
- Button.cs
- DataGridItemEventArgs.cs
- SqlNodeTypeOperators.cs
- DocumentPageHost.cs
- ManifestResourceInfo.cs
- ListViewDataItem.cs
- TextEditorTables.cs
- OdbcDataAdapter.cs
- CollectionContainer.cs
- WebPartTransformer.cs
- CounterCreationData.cs
- TimerElapsedEvenArgs.cs
- ToolboxService.cs
- AutoResizedEvent.cs
- DataChangedEventManager.cs
- ProfileEventArgs.cs
- XPathExpr.cs
- ObjectContext.cs
- HashCryptoHandle.cs
- SymLanguageVendor.cs
- FileVersionInfo.cs
- FormsAuthenticationUserCollection.cs
- RefExpr.cs
- BinaryNode.cs
- MimeParameters.cs
- Helper.cs
- URLAttribute.cs
- SoapHeader.cs
- AssemblyAttributes.cs
- ListViewVirtualItemsSelectionRangeChangedEvent.cs
- HTMLTextWriter.cs
- ModelItem.cs
- RuleAttributes.cs
- SerializationAttributes.cs
- SubclassTypeValidator.cs
- DescendentsWalkerBase.cs
- HeaderCollection.cs
- GridViewRowCollection.cs
- ClientOperationFormatterProvider.cs
- WmlFormAdapter.cs
- CodeValidator.cs
- ContainerSelectorBehavior.cs
- CodeArrayCreateExpression.cs
- ByteAnimationUsingKeyFrames.cs
- ApplicationServiceHelper.cs
- RepeaterItem.cs
- NativeMethods.cs
- ThemeableAttribute.cs
- Repeater.cs
- RichTextBox.cs
- QilUnary.cs
- ModuleBuilder.cs
- DataGridColumnEventArgs.cs
- OverlappedAsyncResult.cs
- IndentTextWriter.cs
- ProxyFragment.cs
- PassportAuthenticationModule.cs
- ImageButton.cs
- _ConnectOverlappedAsyncResult.cs
- ResXResourceReader.cs
- regiisutil.cs
- ResourcePermissionBase.cs
- AnnouncementEndpointElement.cs
- Journaling.cs
- DragStartedEventArgs.cs
- CurrencyWrapper.cs
- DependencyObjectType.cs
- ConfigurationManagerInternalFactory.cs
- CodeArrayIndexerExpression.cs
- HttpApplicationFactory.cs
- UpdatePanel.cs
- FormViewCommandEventArgs.cs
- MetadataSerializer.cs
- JapaneseCalendar.cs
- PasswordPropertyTextAttribute.cs
- DataContext.cs
- SQLUtility.cs
- GridEntry.cs
- WebPart.cs
- TextFormatterContext.cs
- StringSorter.cs
- ListDictionary.cs
- CryptographicAttribute.cs