diff --git a/ICD.Common.Utils/Globalization/IcdCultureInfo.cs b/ICD.Common.Utils/Globalization/IcdCultureInfo.cs new file mode 100644 index 0000000..6d69426 --- /dev/null +++ b/ICD.Common.Utils/Globalization/IcdCultureInfo.cs @@ -0,0 +1,1036 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using ICD.Common.Utils.IO; +using ICD.Common.Utils.Sqlite; + +namespace ICD.Common.Utils.Globalization +{ + /// + /// Adds missing cultures to the limited selection provided by Crestron. + /// + /// + /// This class is a slight modification of CultureEx provided by Neil Colvin in his SSharpCultureLibrary. + /// + /// ICD Changes include: + /// - Using ICD wrapper classes (IO, SQLite, Reflection) to support Net Standard for testing + /// - Removing Neil's custom threading mechanisms (do we *really* want to do threading on Crestron?) + /// - Moving the database location out of NVRAM and into the program directory. + /// + /// + /// Neil Colvin - Actually 1.500.0013 - CultureInfo + /// http://www.crestronlabs.com/showthread.php?11036 + /// + /// It has been very frustrating to live with the limitations of the S#/CF when it comes to internationalization. + /// Crestron has chosen to include no international CultureInfo's in their build of Windows CE. + /// This makes writing generic modules that work in a wide variety of international environments very difficult. + /// To provide a solution to this problem, I have implemented a System.Globolization.CultureInfoEx class that provides + /// many of the missing pieces. + /// + /// CultureInfoEx derives from CultureInfo, so it has the identical properties and methods, both static and instance, + /// It also has the one method missing in the CF build, GetCultures (CultureTypes type) so that all available cultures + /// can be obtained. + /// + /// The important piece behind this is a SQLite database which includes the data for the entire set of CultureInfo's + /// that are available on Windows 10 (809 of them). This database is slightly more the 1 MB in size. + /// + /// This allows CultureInfoEx to be relatively small (23 KB), yet support every culture. Since it is derived from + /// CultureInfo, it can be passed to any method taking a CultureInfo, or an IFormatProvider, as an argument (like + /// String.Format, for instance). + /// + /// When a specific culture is referenced in the CultureInfoEx constructor or in the GetCulture method, it is created + /// on the fly from the SQLite database (and cached in the case of GetCulture so that it does not have to be recreated). + /// + /// The SQLite database is read-only, so there is no difficulty with it being located in the control system internal + /// flash file system (it is currently defined to be located in the \NVRAM\DBs folder). It is totally thread safe as well. + /// + /// If a resident CultureInfo is requested (like "en-US") the built in CultureInfo is used, not the one from the database. + /// + /// ... + /// + /// I have posted this library on my download site as SSharpCultureLibrary. The database is in the Databases Folder as a + /// zip file. It should either be in the same folder on the control system as the library, or if it is to be shared among + /// multiple slots, it should be in the \NVRAM\DBs folder. + /// + /// It should be noted that this does not 100% replicate System.Globalization.CultureInfo, since sealed classes and built-in + /// functionality make that impossible. + /// + /// For instance CompareInfo is a real problem because it is a sealed class. Although CultureInfoEx defaults a CompareInfo + /// in most cases, it is probably not the correct one. Also, any attempt to generate a CompareInfo for any but the "en-US" + /// culture will fail. + /// + /// Also, many .NET functions implicitly use CultureInfo.CurrentCulture as their culture (like String.Format). To use one + /// the CultureInfoEx generated CultureInfo's, they must be used explicitly (all .NET functions which use + /// CultureInfo.CurrentCulture implicitely have a version which takes the CultureInfo explicitely as well). + /// + /// However, this class does provide all of the needed culture specific formatting information needed to display information + /// correctly for that culture (both numbers and data/time). + /// + /// + /// Neil's SSharp/Mono libraries are licensed under MIT: + /// + /// The MIT License (MIT) + /// + /// Copyright (c) 2019 Nivloc Enterprises Ltd + /// + /// Permission is hereby granted, free of charge, to any person obtaining a copy + /// of this software and associated documentation files (the "Software"), to deal + /// in the Software without restriction, including without limitation the rights + /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + /// copies of the Software, and to permit persons to whom the Software is + /// furnished to do so, subject to the following conditions: + /// + /// The above copyright notice and this permission notice shall be included in + /// all copies or substantial portions of the Software. + /// + /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + /// THE SOFTWARE. + /// + public sealed class IcdCultureInfo : CultureInfo + { + private const string SQL_LOCAL_DATABASE_FILE = "CultureInfo.sqlite"; + private const string SQL_CONNECTION_STRING_FORMAT = "Data Source = {0}; Version = 3; ReadOnly = True"; + private const string SQL_CMD_SELECT_BY_NAME = "select * from cultureinfo where name = @name collate nocase"; + private const string SQL_CMD_SELECT_BY_LCID = "select * from cultureinfo where lcid = @lcid"; + private const string SQL_CMD_SELECT_BY_ID = "select * from cultureinfo where id = @id"; + private const string SQL_CMD_SELECT_NUMBER_FORMAT_BY_ID = "select * from numberformatinfo where id = @id"; + private const string SQL_CMD_SELECT_DATE_TIME_FORMAT_BY_ID = "select * from datetimeformatinfo where id = @id"; + + private static readonly Dictionary s_DictAvailableCulturesByName; + private static readonly Dictionary s_DictAvailableCulturesByLcid; + private static readonly Dictionary s_DictSpecificCulture; + private static readonly string[] s_AvailableCultureNames; + private static readonly Dictionary s_DictCacheByName; + private static readonly SafeCriticalSection s_LockCacheByName; + private static readonly Dictionary s_DictCacheByLcid; + private static readonly SafeCriticalSection s_LockCacheByLcid; + private static readonly Dictionary s_DictNumberFormatInfos; + private static readonly Dictionary s_DictDatetimeFormatInfos; + private static readonly string s_SqlConnectionString; + private static readonly bool s_IsDatabasePresent; + + private static CultureInfo s_CurrentCulture; + private static CultureInfo s_CurrentUiCulture; + + private Calendar m_Calendar; + private Type m_CalendarType; + private GregorianCalendarTypes? m_GregorianCalendarType; + private CompareInfo m_CompareInfo; + private string m_EnglishName; + private bool m_IsNeutralCulture; + private int m_Lcid; + private string m_Name; + private string m_NativeName; + private Calendar[] m_OptionalCalendars; + private Type[] m_OptionalCalendarTypes; + private GregorianCalendarTypes?[] m_OptionalGregorianCalendarTypes; + private CultureInfo m_Parent; + private int m_ParentId; + private TextInfo m_TextInfo; + private string m_ThreeLetterIsoLanguageName; + private string m_ThreeLetterWindowsLanguageName; + private string m_TwoLetterIsoLanguageName; + private NumberFormatInfo m_NumberFormat; + private int m_NumberFormatId; + private DateTimeFormatInfo m_DatetimeFormat; + private int m_DatetimeFormatId; + private bool m_IsResident; + + #region Properties + + public new static CultureInfo InvariantCulture { get { return CultureInfo.InvariantCulture; } } + + public new static CultureInfo CurrentCulture + { + get { return s_CurrentCulture ?? CultureInfo.CurrentCulture; } + set + { + if (value == null) + throw new ArgumentNullException("value"); + + s_CurrentCulture = value; + } + } + +// ReSharper disable InconsistentNaming + public new static CultureInfo CurrentUICulture +// ReSharper restore InconsistentNaming + { + get { return s_CurrentUiCulture ?? CultureInfo.CurrentUICulture; } + set + { + if (value == null) + throw new ArgumentNullException("value"); + s_CurrentUiCulture = value; + } + } + + public override Calendar Calendar + { + get + { + if (m_IsResident || m_CalendarType == null) + return base.Calendar; + Calendar calendar; + if ((calendar = m_Calendar) == null) + { + calendar = + (m_Calendar = + ((m_CalendarType == typeof(GregorianCalendar) && m_GregorianCalendarType.HasValue) + ? (ReflectionUtils.CreateInstance(m_CalendarType, new object[] + { + m_GregorianCalendarType + })) + : ReflectionUtils.CreateInstance(m_CalendarType))); + } + return calendar; + } + } + + public override CompareInfo CompareInfo { get { return m_CompareInfo ?? base.CompareInfo; } } + + public override DateTimeFormatInfo DateTimeFormat + { + get + { + if (m_IsResident) + return base.DateTimeFormat; + CheckNeutral(this); + if (m_DatetimeFormat == null) + { + DateTimeFormatInfo dateTimeFormatInfo = GetDateTimeFormat(m_DatetimeFormatId); + if (IsReadOnly) + dateTimeFormatInfo = DateTimeFormatInfo.ReadOnly(dateTimeFormatInfo); + m_DatetimeFormat = dateTimeFormatInfo; + } + return m_DatetimeFormat; + } + set + { + if (m_IsResident) + { + base.DateTimeFormat = value; + return; + } + VerifyWritable(); + if (value == null) + throw new ArgumentException("value"); + m_DatetimeFormat = value; + } + } + + public override string EnglishName { get { return m_EnglishName; } } + + public override bool IsNeutralCulture { get { return m_IsNeutralCulture; } } + + public override int LCID { get { return m_Lcid; } } + + public override string Name { get { return m_Name; } } + + public override string NativeName { get { return m_NativeName; } } + + public override NumberFormatInfo NumberFormat + { + get + { + if (m_IsResident) + return base.NumberFormat; + CheckNeutral(this); + if (m_NumberFormat == null) + { + NumberFormatInfo numberFormatInfo = GetNumberFormat(m_NumberFormatId); + if (IsReadOnly) + numberFormatInfo = NumberFormatInfo.ReadOnly(numberFormatInfo); + m_NumberFormat = numberFormatInfo; + } + return m_NumberFormat; + } + set + { + if (m_IsResident) + { + base.NumberFormat = value; + return; + } + VerifyWritable(); + if (value == null) + throw new ArgumentException("value"); + m_NumberFormat = value; + } + } + + public override Calendar[] OptionalCalendars + { + get + { + Calendar[] calendars; + if ((calendars = m_OptionalCalendars) == null) + { + calendars = + (m_OptionalCalendars = + m_OptionalCalendarTypes.Select((t, ix) => + new KeyValuePair + (t, + m_OptionalGregorianCalendarTypes + [ix])).Where(kvp => kvp.Key != null).Select(delegate(KeyValuePair kvp) + { + if (kvp.Key != typeof(GregorianCalendar) || !kvp.Value.HasValue) + return ReflectionUtils.CreateInstance(kvp.Key); + return ReflectionUtils.CreateInstance(kvp.Key, new object[] + { + kvp.Value + }); + }).ToArray()); + } + return calendars; + } + } + + public override CultureInfo Parent + { + get + { + CultureInfo cultureInfo; + if ((cultureInfo = m_Parent) == null) + cultureInfo = (m_Parent = new IcdCultureInfo(m_ParentId, 0)); + return cultureInfo; + } + } + + public override TextInfo TextInfo { get { return m_TextInfo ?? base.TextInfo; } } + + public override string ThreeLetterISOLanguageName { get { return m_ThreeLetterIsoLanguageName; } } + + public override string ThreeLetterWindowsLanguageName { get { return m_ThreeLetterWindowsLanguageName; } } + + public override string TwoLetterISOLanguageName { get { return m_TwoLetterIsoLanguageName; } } + + #endregion + + #region Constructors + + static IcdCultureInfo() + { + s_DictAvailableCulturesByName = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + s_DictAvailableCulturesByLcid = new Dictionary(); + s_DictSpecificCulture = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + s_DictCacheByName = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + s_LockCacheByName = new SafeCriticalSection(); + s_DictCacheByLcid = new Dictionary(); + s_LockCacheByLcid = new SafeCriticalSection(); + s_DictNumberFormatInfos = new Dictionary(); + s_DictDatetimeFormatInfos = new Dictionary(); + string[] builtinCultures = + { + "", + "af", + "ar", + "az", + "be", + "bg", + "ca", + "cs", + "da", + "de", + "dv", + "el", + "en", + "en-US", + "es", + "et", + "eu", + "fa", + "fi", + "fo", + "fr", + "gl", + "gu", + "he", + "hi", + "hr", + "hu", + "hy", + "id", + "is", + "it", + "ja", + "ka", + "kk", + "kn", + "ko", + "kok", + "ky", + "lt", + "lv", + "mk", + "mn", + "mr", + "ms", + "nl", + "no", + "pa", + "pl", + "pt", + "ro", + "ru", + "sa", + "sk", + "sl", + "sq", + "sr", + "sv", + "sw", + "syr", + "ta", + "te", + "th", + "tr", + "tt", + "uk", + "ur", + "uz", + "vi", + "zh-Hans", + "zh-Hant", + "zh-CHS", + "zh-CHT" + }; + + string databasePath = IcdPath.Combine(PathUtils.ProgramPath, SQL_LOCAL_DATABASE_FILE); + if (!IcdFile.Exists(databasePath)) + { + s_IsDatabasePresent = false; + return; + } + + s_SqlConnectionString = string.Format(SQL_CONNECTION_STRING_FORMAT, databasePath); + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + try + { + sQLiteConnection.Open(); + } + catch (Exception) + { + s_IsDatabasePresent = false; + return; + } + s_IsDatabasePresent = true; + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand("select count(*) from cultureinfo", sQLiteConnection); + int num = Convert.ToInt32(sQLiteCommand.ExecuteScalar()); + s_AvailableCultureNames = new string[num + 1]; + IcdSqliteCommand sQLiteCommand2 = new IcdSqliteCommand("select id, name, lcid, isneutralculture from cultureinfo", + sQLiteConnection); + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand2.ExecuteReader()) + { + while (sQLiteDataReader.Read()) + { + int @int = sQLiteDataReader.GetInt32(0); + bool boolean = sQLiteDataReader.GetBoolean(3); + string @string = sQLiteDataReader.GetString(1); + s_DictAvailableCulturesByName[@string] = (boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures); + int int2 = sQLiteDataReader.GetInt32(2); + s_DictAvailableCulturesByLcid[int2] = (boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures); + s_AvailableCultureNames[@int] = @string; + } + } + sQLiteCommand2 = new IcdSqliteCommand("select id, specificculture from specificcultureinfo", sQLiteConnection); + using (IcdSqliteDataReader sQLiteDataReader2 = sQLiteCommand2.ExecuteReader()) + { + while (sQLiteDataReader2.Read()) + { + int int3 = sQLiteDataReader2.GetInt32(0); + string string2 = sQLiteDataReader2.GetString(1); + s_DictSpecificCulture[s_AvailableCultureNames[int3]] = string2; + } + } + } + string[] array = builtinCultures; + for (int i = 0; i < array.Length; i++) + { + string name = array[i]; + try + { + CultureInfo cultureInfo = CultureInfo.GetCultureInfo(name); + Dictionary dictAvailableCulturesByName; + string name2; + (dictAvailableCulturesByName = s_DictAvailableCulturesByName)[name2 = cultureInfo.Name] = + (dictAvailableCulturesByName[name2] | CultureTypes.InstalledWin32Cultures); + Dictionary dictAvailableCulturesByLcid; + int lCid; + (dictAvailableCulturesByLcid = s_DictAvailableCulturesByLcid)[lCid = cultureInfo.LCID] = + (dictAvailableCulturesByLcid[lCid] | CultureTypes.InstalledWin32Cultures); + } + catch (Exception) + { + } + } + } + + public IcdCultureInfo(int culture) + : this(culture, true) + { + } + + public IcdCultureInfo(int culture, bool useUserOverride) + : base(GetResidentNeutralLcid(culture), useUserOverride) + { + if (culture < 0) + throw new ArgumentOutOfRangeException("culture", "must be >= 0"); + CultureTypes cultureTypes; + if (!s_DictAvailableCulturesByLcid.TryGetValue(culture, out cultureTypes)) + throw new ArgumentException("not supported"); + if (!s_IsDatabasePresent || (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0) + { + BuildCultureInfoEx(CultureInfo.GetCultureInfo(culture)); + return; + } + CultureInfo ci; + bool flag; + s_LockCacheByLcid.Enter(); + { + flag = s_DictCacheByLcid.TryGetValue(culture, out ci); + } + s_LockCacheByLcid.Leave(); + if (flag) + { + BuildCultureInfoEx(ci); + return; + } + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + sQLiteConnection.Open(); + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand(SQL_CMD_SELECT_BY_LCID, sQLiteConnection); + sQLiteCommand.Parameters.AddWithValue("@lcid", culture); + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand.ExecuteReader()) + ProcessReader(sQLiteDataReader, sQLiteConnection); + } + } + + public IcdCultureInfo(string name) + : this(name, true) + { + } + + public IcdCultureInfo(string name, bool useUserOverrides) + : base(GetResidentNeutralName(name), useUserOverrides) + { + if (name == null) + throw new ArgumentNullException("name"); + CultureTypes cultureTypes; + if (!s_DictAvailableCulturesByName.TryGetValue(name, out cultureTypes)) + throw new ArgumentException("not supported"); + if (!s_IsDatabasePresent || (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0) + { + BuildCultureInfoEx(CultureInfo.GetCultureInfo(name)); + return; + } + CultureInfo ci; + bool flag; + s_LockCacheByName.Enter(); + { + flag = s_DictCacheByName.TryGetValue(name, out ci); + } + s_LockCacheByName.Leave(); + if (flag) + { + BuildCultureInfoEx(ci); + return; + } + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + sQLiteConnection.Open(); + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand(SQL_CMD_SELECT_BY_NAME, sQLiteConnection); + sQLiteCommand.Parameters.AddWithValue("@name", name); + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand.ExecuteReader()) + ProcessReader(sQLiteDataReader, sQLiteConnection); + } + } + + private IcdCultureInfo(int id, int clone) + : base(GetResidentNeutralName(s_AvailableCultureNames[id])) + { + if (!s_IsDatabasePresent) + throw new PlatformNotSupportedException("No database"); + if (IsResident(s_AvailableCultureNames[id])) + { + BuildCultureInfoEx(CultureInfo.GetCultureInfo(s_AvailableCultureNames[id])); + return; + } + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + sQLiteConnection.Open(); + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand(SQL_CMD_SELECT_BY_ID, sQLiteConnection); + sQLiteCommand.Parameters.AddWithValue("@id", id); + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand.ExecuteReader()) + ProcessReader(sQLiteDataReader, sQLiteConnection); + } + } + + private IcdCultureInfo(CultureInfo ci) + : base(GetResidentNeutralName(ci.Name)) + { + BuildCultureInfoEx(ci); + } + + public static IcdCultureInfo FromCultureInfo(CultureInfo ci) + { + return new IcdCultureInfo(ci); + } + + #endregion + + private void ProcessReader(IcdSqliteDataReader rdr, IcdSqliteConnection conn) + { + if (!rdr.Read()) + throw new InvalidOperationException("failure reading database"); + int ordinal = rdr.GetOrdinal("calendar"); + string text = rdr.GetString(ordinal); + if (text.EndsWith(")")) + { + int num = text.IndexOf('('); + string value = text.Substring(num + 1, text.Length - num - 2); + text = text.Substring(0, num); + m_GregorianCalendarType = (GregorianCalendarTypes)Enum.Parse(typeof(GregorianCalendarTypes), value, true); + } + m_CalendarType = Type.GetType("System.Globalization." + text); + ordinal = rdr.GetOrdinal("englishname"); + m_EnglishName = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("isneutralculture"); + m_IsNeutralCulture = rdr.GetBoolean(ordinal); + ordinal = rdr.GetOrdinal("lcid"); + m_Lcid = rdr.GetInt32(ordinal); + ordinal = rdr.GetOrdinal("name"); + m_Name = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("nativename"); + m_NativeName = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("optionalcalendars"); + string[] array = rdr.GetString(ordinal).Split(new[] + { + '|' + }); + m_OptionalGregorianCalendarTypes = new GregorianCalendarTypes?[array.Length]; + m_OptionalCalendarTypes = new Type[array.Length]; + for (int i = 0; i < array.Length; i++) + { + string text2 = array[i]; + if (text2.EndsWith(")")) + { + int num2 = text2.IndexOf('('); + string value2 = text2.Substring(num2 + 1, text2.Length - num2 - 2); + text2 = text2.Substring(0, num2); + m_OptionalGregorianCalendarTypes[i] = + (GregorianCalendarTypes)Enum.Parse(typeof(GregorianCalendarTypes), value2, true); + } + m_OptionalCalendarTypes[i] = Type.GetType("System.Globalization." + text2); + } + ordinal = rdr.GetOrdinal("parent"); + m_ParentId = rdr.GetInt32(ordinal); + ordinal = rdr.GetOrdinal("threeletterisolanguagename"); + m_ThreeLetterIsoLanguageName = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("threeletterwindowslanguagename"); + m_ThreeLetterWindowsLanguageName = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("twoletterisolanguagename"); + m_TwoLetterIsoLanguageName = rdr.GetString(ordinal); + ordinal = rdr.GetOrdinal("datetimeformat"); + m_DatetimeFormatId = rdr.GetInt32(ordinal); + ordinal = rdr.GetOrdinal("numberformat"); + m_NumberFormatId = rdr.GetInt32(ordinal); + rdr.Close(); + } + + private static NumberFormatInfo GetNumberFormat(int id) + { + NumberFormatInfo numberFormat; + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + sQLiteConnection.Open(); + numberFormat = GetNumberFormat(id, sQLiteConnection); + } + return numberFormat; + } + + private static NumberFormatInfo GetNumberFormat(int id, IcdSqliteConnection conn) + { + NumberFormatInfo numberFormatInfo; + if (s_DictNumberFormatInfos.TryGetValue(id, out numberFormatInfo)) + return (NumberFormatInfo)numberFormatInfo.Clone(); + + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand(SQL_CMD_SELECT_NUMBER_FORMAT_BY_ID, conn); + sQLiteCommand.Parameters.AddWithValue("@id", id); + + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand.ExecuteReader()) + { + if (!sQLiteDataReader.Read()) + throw new InvalidOperationException("invalid number format database"); + + int ordinal = sQLiteDataReader.GetOrdinal("CurrencyDecimalDigits"); + int ordinal2 = sQLiteDataReader.GetOrdinal("CurrencyDecimalSeparator"); + int ordinal3 = sQLiteDataReader.GetOrdinal("CurrencyGroupSizes"); + int ordinal4 = sQLiteDataReader.GetOrdinal("NumberGroupSizes"); + int ordinal5 = sQLiteDataReader.GetOrdinal("PercentGroupSizes"); + int ordinal6 = sQLiteDataReader.GetOrdinal("CurrencyGroupSeparator"); + int ordinal7 = sQLiteDataReader.GetOrdinal("CurrencySymbol"); + int ordinal8 = sQLiteDataReader.GetOrdinal("NaNSymbol"); + int ordinal9 = sQLiteDataReader.GetOrdinal("CurrencyNegativePattern"); + int ordinal10 = sQLiteDataReader.GetOrdinal("NumberNegativePattern"); + int ordinal11 = sQLiteDataReader.GetOrdinal("PercentPositivePattern"); + int ordinal12 = sQLiteDataReader.GetOrdinal("PercentNegativePattern"); + int ordinal13 = sQLiteDataReader.GetOrdinal("NegativeInfinitySymbol"); + int ordinal14 = sQLiteDataReader.GetOrdinal("NegativeSign"); + int ordinal15 = sQLiteDataReader.GetOrdinal("NumberDecimalDigits"); + int ordinal16 = sQLiteDataReader.GetOrdinal("NumberDecimalSeparator"); + int ordinal17 = sQLiteDataReader.GetOrdinal("NumberGroupSeparator"); + int ordinal18 = sQLiteDataReader.GetOrdinal("CurrencyPositivePattern"); + int ordinal19 = sQLiteDataReader.GetOrdinal("PositiveInfinitySymbol"); + int ordinal20 = sQLiteDataReader.GetOrdinal("PositiveSign"); + int ordinal21 = sQLiteDataReader.GetOrdinal("PercentDecimalDigits"); + int ordinal22 = sQLiteDataReader.GetOrdinal("PercentDecimalSeparator"); + int ordinal23 = sQLiteDataReader.GetOrdinal("PercentGroupSeparator"); + int ordinal24 = sQLiteDataReader.GetOrdinal("PercentSymbol"); + int ordinal25 = sQLiteDataReader.GetOrdinal("PerMilleSymbol"); + + NumberFormatInfo numberFormatInfo2 = new NumberFormatInfo + { + CurrencyDecimalDigits = sQLiteDataReader.GetInt32(ordinal), + CurrencyDecimalSeparator = sQLiteDataReader.GetString(ordinal2), + CurrencyGroupSizes = sQLiteDataReader.GetString(ordinal3) + .Split(new[] {','}) + .Select(s => int.Parse(s)) + .ToArray(), + NumberGroupSizes = sQLiteDataReader.GetString(ordinal4) + .Split(new[] {','}) + .Select(s => int.Parse(s)) + .ToArray(), + PercentGroupSizes = sQLiteDataReader.GetString(ordinal5) + .Split(new[] {','}) + .Select(s => int.Parse(s)) + .ToArray(), + CurrencyGroupSeparator = sQLiteDataReader.GetString(ordinal6), + CurrencySymbol = sQLiteDataReader.GetString(ordinal7), + NaNSymbol = sQLiteDataReader.GetString(ordinal8), + CurrencyNegativePattern = sQLiteDataReader.GetInt32(ordinal9), + NumberNegativePattern = sQLiteDataReader.GetInt32(ordinal10), + PercentPositivePattern = sQLiteDataReader.GetInt32(ordinal11), + PercentNegativePattern = sQLiteDataReader.GetInt32(ordinal12), + NegativeInfinitySymbol = sQLiteDataReader.GetString(ordinal13), + NegativeSign = sQLiteDataReader.GetString(ordinal14), + NumberDecimalDigits = sQLiteDataReader.GetInt32(ordinal15), + NumberDecimalSeparator = sQLiteDataReader.GetString(ordinal16), + NumberGroupSeparator = sQLiteDataReader.GetString(ordinal17), + CurrencyPositivePattern = sQLiteDataReader.GetInt32(ordinal18), + PositiveInfinitySymbol = sQLiteDataReader.GetString(ordinal19), + PositiveSign = sQLiteDataReader.GetString(ordinal20), + PercentDecimalDigits = sQLiteDataReader.GetInt32(ordinal21), + PercentDecimalSeparator = sQLiteDataReader.GetString(ordinal22), + PercentGroupSeparator = sQLiteDataReader.GetString(ordinal23), + PercentSymbol = sQLiteDataReader.GetString(ordinal24), + PerMilleSymbol = sQLiteDataReader.GetString(ordinal25) + }; + numberFormatInfo = numberFormatInfo2; + } + s_DictNumberFormatInfos[id] = numberFormatInfo; + return (NumberFormatInfo)numberFormatInfo.Clone(); + } + + private static DateTimeFormatInfo GetDateTimeFormat(int id) + { + DateTimeFormatInfo dateTimeFormat; + using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString)) + { + sQLiteConnection.Open(); + dateTimeFormat = GetDateTimeFormat(id, sQLiteConnection); + } + return dateTimeFormat; + } + + private static DateTimeFormatInfo GetDateTimeFormat(int id, IcdSqliteConnection conn) + { + DateTimeFormatInfo dateTimeFormatInfo; + if (s_DictDatetimeFormatInfos.TryGetValue(id, out dateTimeFormatInfo)) + return (DateTimeFormatInfo)dateTimeFormatInfo.Clone(); + IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand(SQL_CMD_SELECT_DATE_TIME_FORMAT_BY_ID, conn); + sQLiteCommand.Parameters.AddWithValue("@id", id); + using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand.ExecuteReader()) + { + if (!sQLiteDataReader.Read()) + throw new InvalidOperationException("invalid datetime format database"); + int ordinal = sQLiteDataReader.GetOrdinal("AMDesignator"); + sQLiteDataReader.GetOrdinal("Calendar"); + int ordinal2 = sQLiteDataReader.GetOrdinal("DateSeparator"); + int ordinal3 = sQLiteDataReader.GetOrdinal("FirstDayOfWeek"); + int ordinal4 = sQLiteDataReader.GetOrdinal("CalendarWeekRule"); + int ordinal5 = sQLiteDataReader.GetOrdinal("FullDateTimePattern"); + int ordinal6 = sQLiteDataReader.GetOrdinal("LongDatePattern"); + int ordinal7 = sQLiteDataReader.GetOrdinal("LongTimePattern"); + int ordinal8 = sQLiteDataReader.GetOrdinal("MonthDayPattern"); + int ordinal9 = sQLiteDataReader.GetOrdinal("PMDesignator"); + int ordinal10 = sQLiteDataReader.GetOrdinal("ShortDatePattern"); + int ordinal11 = sQLiteDataReader.GetOrdinal("ShortTimePattern"); + int ordinal12 = sQLiteDataReader.GetOrdinal("TimeSeparator"); + int ordinal13 = sQLiteDataReader.GetOrdinal("YearMonthPattern"); + int ordinal14 = sQLiteDataReader.GetOrdinal("AbbreviatedDayNames"); + int ordinal15 = sQLiteDataReader.GetOrdinal("ShortestDayNames"); + int ordinal16 = sQLiteDataReader.GetOrdinal("DayNames"); + int ordinal17 = sQLiteDataReader.GetOrdinal("AbbreviatedMonthNames"); + int ordinal18 = sQLiteDataReader.GetOrdinal("MonthNames"); + int ordinal19 = sQLiteDataReader.GetOrdinal("AbbreviatedMonthGenitiveNames"); + int ordinal20 = sQLiteDataReader.GetOrdinal("MonthGenitiveNames"); + dateTimeFormatInfo = new DateTimeFormatInfo + { + AMDesignator = sQLiteDataReader.GetString(ordinal), + DateSeparator = sQLiteDataReader.GetString(ordinal2), + FirstDayOfWeek = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), sQLiteDataReader.GetString(ordinal3), true), + CalendarWeekRule = + (CalendarWeekRule)Enum.Parse(typeof(CalendarWeekRule), sQLiteDataReader.GetString(ordinal4), true), + FullDateTimePattern = sQLiteDataReader.GetString(ordinal5), + LongDatePattern = sQLiteDataReader.GetString(ordinal6), + LongTimePattern = sQLiteDataReader.GetString(ordinal7), + MonthDayPattern = sQLiteDataReader.GetString(ordinal8), + PMDesignator = sQLiteDataReader.GetString(ordinal9), + ShortDatePattern = sQLiteDataReader.GetString(ordinal10), + ShortTimePattern = sQLiteDataReader.GetString(ordinal11), + TimeSeparator = sQLiteDataReader.GetString(ordinal12), + YearMonthPattern = sQLiteDataReader.GetString(ordinal13), + AbbreviatedDayNames = sQLiteDataReader.GetString(ordinal14).Split(new[] + { + '|' + }), + ShortestDayNames = sQLiteDataReader.GetString(ordinal15).Split(new[] + { + '|' + }), + DayNames = sQLiteDataReader.GetString(ordinal16).Split(new[] + { + '|' + }), + AbbreviatedMonthNames = sQLiteDataReader.GetString(ordinal17).Split(new[] + { + '|' + }), + MonthNames = sQLiteDataReader.GetString(ordinal18).Split(new[] + { + '|' + }), + AbbreviatedMonthGenitiveNames = sQLiteDataReader.GetString(ordinal19).Split(new[] + { + '|' + }), + MonthGenitiveNames = sQLiteDataReader.GetString(ordinal20).Split(new[] + { + '|' + }) + }; + } + s_DictDatetimeFormatInfos[id] = dateTimeFormatInfo; + return (DateTimeFormatInfo)dateTimeFormatInfo.Clone(); + } + + private void BuildCultureInfoEx(CultureInfo ci) + { + IcdCultureInfo icdCultureInfo = ci as IcdCultureInfo; + if (icdCultureInfo == null) + { + m_IsResident = true; + m_Calendar = ci.Calendar; + m_OptionalCalendars = ci.OptionalCalendars; + m_Parent = ci.Parent; + m_TextInfo = ci.TextInfo; + try + { + m_CompareInfo = ci.CompareInfo; + goto IL_CE; + } + catch (PlatformNotSupportedException) + { + m_CompareInfo = CultureInfo.InvariantCulture.CompareInfo; + goto IL_CE; + } + } + m_Calendar = icdCultureInfo.m_Calendar; + m_CalendarType = icdCultureInfo.m_CalendarType; + m_GregorianCalendarType = icdCultureInfo.m_GregorianCalendarType; + m_OptionalCalendars = icdCultureInfo.m_OptionalCalendars; + m_OptionalCalendarTypes = icdCultureInfo.m_OptionalCalendarTypes; + m_OptionalGregorianCalendarTypes = icdCultureInfo.m_OptionalGregorianCalendarTypes; + m_Parent = icdCultureInfo.m_Parent; + m_TextInfo = icdCultureInfo.m_TextInfo; + m_CompareInfo = icdCultureInfo.m_CompareInfo; + IL_CE: + m_EnglishName = ci.EnglishName; + m_IsNeutralCulture = ci.IsNeutralCulture; + m_Lcid = ci.LCID; + m_Name = ci.Name; + m_NativeName = ci.NativeName; + m_ThreeLetterIsoLanguageName = ci.ThreeLetterISOLanguageName; + m_ThreeLetterWindowsLanguageName = ci.ThreeLetterWindowsLanguageName; + m_TwoLetterIsoLanguageName = ci.TwoLetterISOLanguageName; + if (!m_IsNeutralCulture) + { + if (icdCultureInfo == null) + { + m_DatetimeFormat = ci.DateTimeFormat; + m_NumberFormat = ci.NumberFormat; + return; + } + m_DatetimeFormatId = icdCultureInfo.m_DatetimeFormatId; + m_DatetimeFormat = icdCultureInfo.m_DatetimeFormat; + m_NumberFormatId = icdCultureInfo.m_NumberFormatId; + m_NumberFormat = icdCultureInfo.m_NumberFormat; + } + } + + #region Methods + + public new static CultureInfo GetCultureInfo(string name) + { + CultureInfo cultureInfo; + s_LockCacheByName.Enter(); + { + if (s_DictCacheByName.TryGetValue(name, out cultureInfo)) + return cultureInfo; + } + s_LockCacheByName.Leave(); + cultureInfo = new IcdCultureInfo(name); + if (!cultureInfo.IsNeutralCulture) + cultureInfo = ReadOnly(cultureInfo); + s_LockCacheByName.Enter(); + { + s_DictCacheByName[cultureInfo.Name] = cultureInfo; + s_DictCacheByLcid[cultureInfo.LCID] = cultureInfo; + } + s_LockCacheByName.Leave(); + return cultureInfo; + } + + public new static CultureInfo GetCultureInfo(int culture) + { + CultureInfo cultureInfo; + s_LockCacheByLcid.Enter(); + { + if (s_DictCacheByLcid.TryGetValue(culture, out cultureInfo)) + return cultureInfo; + } + s_LockCacheByLcid.Leave(); + cultureInfo = new IcdCultureInfo(culture); + if (!cultureInfo.IsNeutralCulture) + cultureInfo = ReadOnly(cultureInfo); + s_LockCacheByLcid.Enter(); + { + s_DictCacheByName[cultureInfo.Name] = cultureInfo; + s_DictCacheByLcid[cultureInfo.LCID] = cultureInfo; + } + s_LockCacheByLcid.Leave(); + return cultureInfo; + } + + public static CultureInfo[] GetCultures(CultureTypes types) + { + return s_DictAvailableCulturesByName.Where(de => (de.Value & types) != (CultureTypes)0) + .Select(de => new IcdCultureInfo(de.Key)) + .Cast() + .ToArray(); + } + + public new static CultureInfo CreateSpecificCulture(string name) + { + IcdCultureInfo icdCultureInfo = new IcdCultureInfo(name); + if (!icdCultureInfo.IsNeutralCulture) + return icdCultureInfo; + if ((icdCultureInfo.LCID & 1023) == 4) + throw new ArgumentException(); + return new IcdCultureInfo(s_DictSpecificCulture[name]); + } + + public override object Clone() + { + return new IcdCultureInfo(this); + } + + public override bool Equals(object value) + { + CultureInfo cultureInfo = value as CultureInfo; + return cultureInfo != null && Name.Equals(cultureInfo.Name) && CompareInfo.Equals(cultureInfo.CompareInfo); + } + + public override int GetHashCode() + { + return Name.GetHashCode() + CompareInfo.GetHashCode(); + } + + public override string ToString() + { + return m_Name; + } + + public new void ClearCachedData() + { + s_LockCacheByName.Enter(); + s_LockCacheByLcid.Enter(); + { + s_DictCacheByName.Clear(); + s_DictCacheByLcid.Clear(); + } + s_LockCacheByLcid.Enter(); + s_LockCacheByName.Enter(); + + base.ClearCachedData(); + } + + #endregion + + private void VerifyWritable() + { + if (IsReadOnly) + throw new InvalidOperationException(); + } + + private static void CheckNeutral(CultureInfo culture) + { + if (culture.IsNeutralCulture) + throw new NotSupportedException(); + } + + private static bool IsResident(string name) + { + CultureTypes cultureTypes; + return s_DictAvailableCulturesByName.TryGetValue(name, out cultureTypes) && + (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0; + } + + private static bool IsResidentAndNeutral(string name) + { + CultureTypes cultureTypes; + return s_DictAvailableCulturesByName.TryGetValue(name, out cultureTypes) && + (cultureTypes & (CultureTypes.InstalledWin32Cultures | CultureTypes.NeutralCultures)) == + (CultureTypes.InstalledWin32Cultures | CultureTypes.NeutralCultures); + } + + private static string GetResidentNeutralName(string name) + { + if (IsResident(name)) + return name; + if (name.Length <= 2 || !IsResidentAndNeutral(name.Substring(0, 2))) + return string.Empty; + return name.Substring(0, 2); + } + + private static bool IsResident(int culture) + { + CultureTypes cultureTypes; + return s_DictAvailableCulturesByLcid.TryGetValue(culture, out cultureTypes) && + (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0; + } + + private static int GetResidentNeutralLcid(int culture) + { + return IsResident(culture) ? culture : 127; + } + } +} diff --git a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj index 31eb3a4..57062b1 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj @@ -45,5 +45,10 @@ + + + PreserveNewest + + \ No newline at end of file diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 80364f1..071e206 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -112,6 +112,7 @@ + @@ -218,6 +219,9 @@ + + PreserveNewest + diff --git a/ICD.Common.Utils/Resources/CultureInfo.sqlite b/ICD.Common.Utils/Resources/CultureInfo.sqlite new file mode 100644 index 0000000..44f8f00 Binary files /dev/null and b/ICD.Common.Utils/Resources/CultureInfo.sqlite differ