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()
{
//
if (SyntheticLcidToNameCache == null || SyntheticNameToLcidCache == null)
{
CacheSyntheticNameLcidMapping();
}
}
//
#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 (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 (i1);
}
//
// 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; iiDigits);} } // (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
- SqlServices.cs
- RotationValidation.cs
- SelectionWordBreaker.cs
- SafeCancelMibChangeNotify.cs
- TextDpi.cs
- DataServiceEntityAttribute.cs
- dataSvcMapFileLoader.cs
- SubtreeProcessor.cs
- LinkUtilities.cs
- DescriptionCreator.cs
- ContentType.cs
- VirtualPath.cs
- XmlAttributeProperties.cs
- DataGridViewCellValidatingEventArgs.cs
- GeometryDrawing.cs
- TypeLoadException.cs
- ProviderCollection.cs
- SimpleApplicationHost.cs
- HierarchicalDataSourceDesigner.cs
- StringValidatorAttribute.cs
- Screen.cs
- CompositeFontInfo.cs
- BaseProcessor.cs
- SelectedGridItemChangedEvent.cs
- ObjectTokenCategory.cs
- PreloadHost.cs
- AdPostCacheSubstitution.cs
- AuthenticateEventArgs.cs
- ExpressionLexer.cs
- ListViewCommandEventArgs.cs
- BitmapEffectDrawing.cs
- TextDecorations.cs
- FileDialog.cs
- WebBrowserNavigatedEventHandler.cs
- LogLogRecord.cs
- ListDictionary.cs
- Scanner.cs
- CultureTable.cs
- LongMinMaxAggregationOperator.cs
- CompoundFileIOPermission.cs
- ExpanderAutomationPeer.cs
- CrossSiteScriptingValidation.cs
- RoleGroup.cs
- AuthenticationServiceManager.cs
- GridViewAutoFormat.cs
- GridPattern.cs
- ParameterBuilder.cs
- ListSortDescriptionCollection.cs
- Base64Stream.cs
- InfoCardAsymmetricCrypto.cs
- PresentationSource.cs
- BitmapEffectInput.cs
- CngUIPolicy.cs
- UniqueID.cs
- IssuedTokenClientCredential.cs
- StructuredType.cs
- HandleExceptionArgs.cs
- ThreadStaticAttribute.cs
- TextEmbeddedObject.cs
- ImageField.cs
- RadialGradientBrush.cs
- RouteParametersHelper.cs
- StyleTypedPropertyAttribute.cs
- FileClassifier.cs
- Application.cs
- XmlNamespaceManager.cs
- smtpconnection.cs
- FontCollection.cs
- InvariantComparer.cs
- UnsafeNativeMethods.cs
- FormViewInsertEventArgs.cs
- XsltOutput.cs
- LayoutDump.cs
- GridViewDeleteEventArgs.cs
- ReferenceEqualityComparer.cs
- PerformanceCounterPermissionAttribute.cs
- SwitchLevelAttribute.cs
- CounterCreationDataCollection.cs
- SafeRightsManagementPubHandle.cs
- PrinterResolution.cs
- ByteStream.cs
- BaseParaClient.cs
- TextShapeableCharacters.cs
- RuntimeArgumentHandle.cs
- SynchronizationValidator.cs
- CommonProperties.cs
- SingleObjectCollection.cs
- XmlSchemaProviderAttribute.cs
- AddingNewEventArgs.cs
- PageBreakRecord.cs
- ProfileProvider.cs
- TreeIterators.cs
- Interlocked.cs
- BamlLocalizer.cs
- AlphabetConverter.cs
- CssStyleCollection.cs
- ErrorLog.cs
- Color.cs
- RadioButtonRenderer.cs
- ToolStripRenderEventArgs.cs