From 8d11139bfef11a2319fe8ee0e2d7f4c9360849d5 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 26 Dec 2021 00:56:54 +0700 Subject: [PATCH 1/2] Directory.Build.targets: Removed FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTCULTURE and FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTUICULTURE, since they are not supported anyway --- Directory.Build.targets | 2 -- src/J2N/Text/StringFormatter.cs | 20 -------------------- 2 files changed, 22 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 8d7a4428..a19b9c18 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -132,8 +132,6 @@ - $(DefineConstants);FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTCULTURE - $(DefineConstants);FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTUICULTURE $(DefineConstants);FEATURE_EXCEPTIONDISPATCHINFO $(DefineConstants);FEATURE_IREADONLYCOLLECTIONS $(DefineConstants);FEATURE_METHODIMPLOPTIONS_AGRESSIVEINLINING diff --git a/src/J2N/Text/StringFormatter.cs b/src/J2N/Text/StringFormatter.cs index fd04be5c..1a984ab1 100644 --- a/src/J2N/Text/StringFormatter.cs +++ b/src/J2N/Text/StringFormatter.cs @@ -52,16 +52,6 @@ public class StringFormatter : IFormatProvider, ICustomFormatter /// public static StringFormatter CurrentUICulture { get; } = new StringFormatter(CultureType.CurrentUICulture); - ///// - ///// Gets a that uses the default culture for threads in the current application domain to format values. - ///// - //public static StringFormatter DefaultThreadCurrentCulture { get; } = new StringFormatter(CultureType.DefaultThreadCurrentCulture); - - ///// - ///// Gets a that uses the default UI culture for threads in the current application domain to format values. - ///// - //public static StringFormatter DefaultThreadCurrentUICulture { get; } = new StringFormatter(CultureType.DefaultThreadCurrentUICulture); - /// /// Gets a that uses the invariant culture to format values. /// This is the default setting in Java. @@ -116,14 +106,6 @@ protected virtual CultureInfo Culture return CultureInfo.CurrentCulture; case CultureType.CurrentUICulture: return CultureInfo.CurrentUICulture; -#if FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTCULTURE - case CultureType.DefaultThreadCurrentCulture: - return CultureInfo.DefaultThreadCurrentCulture ?? CultureInfo.CurrentCulture; -#endif -#if FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTUICULTURE - case CultureType.DefaultThreadCurrentUICulture: - return CultureInfo.DefaultThreadCurrentUICulture ?? CultureInfo.CurrentUICulture; -#endif default: return CultureInfo.CurrentCulture; } @@ -134,8 +116,6 @@ internal enum CultureType { CurrentCulture, CurrentUICulture, - DefaultThreadCurrentCulture, - DefaultThreadCurrentUICulture, InvariantCulture, CustomCulture } From e8e2c09acd53360be196ecbe682b472c25a2af94 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 26 Dec 2021 02:06:09 +0700 Subject: [PATCH 2/2] J2N.Text.StringFormatter: Added constructor overload for IFormatProvider and deprecated Culture protected property, since it is easily possible to store the CultureInfo reference in a subclass when calling the appropriate constructor. --- Directory.Build.targets | 3 +- src/J2N/Text/StringFormatter.cs | 65 +++++++++++++++++++-- tests/J2N.Tests/Text/TestStringFormatter.cs | 10 ++++ 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index a19b9c18..ad79076b 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -22,7 +22,8 @@ - + + $(DefineConstants);FEATURE_CULTUREINFO_PREDEFINEDONLY $(DefineConstants);FEATURE_HASHSET_MODIFY_CONTINUEENUMERATION $(DefineConstants);FEATURE_ICU diff --git a/src/J2N/Text/StringFormatter.cs b/src/J2N/Text/StringFormatter.cs index 1a984ab1..2992f0a7 100644 --- a/src/J2N/Text/StringFormatter.cs +++ b/src/J2N/Text/StringFormatter.cs @@ -65,6 +65,8 @@ public class StringFormatter : IFormatProvider, ICustomFormatter private CultureInfo? culture; // not readonly for deserialization private readonly CultureType? cultureType; + private IFormatProvider? formatProvider; // NOTE: This needs to be [Serializable] to support serialization. Note on .NET Core serialization has been dropped on these implementations. + /// /// Initializes a new instance of . /// @@ -74,6 +76,10 @@ public StringFormatter() /// /// Initializes a new instance of with the specified . + /// + /// NOTE: This overload only supports serialization of built-in cultures. If you require serialization and have a custom implementation, + /// you will need to provide a serializable wrapper to the constructor. Note that + /// on .NET Core and newer .NET platforms serialization is not supported for , and . /// /// A that specifies the culture-specific rules that will be used for formatting. /// If is null. @@ -84,6 +90,21 @@ public StringFormatter(CultureInfo culture) this.cultureSymbol = this.culture.Name.ToCharArray(); // For deserialization } + /// + /// Initializes a new instance of with the specified . + /// + /// NOTE: If binary serialization is required, the type passed must be annotated with the and otherwise be + /// setup for serialization and deserialization. However, note that on .NET Core and newer .NET platforms serialization is not supported for + /// , and . + /// + /// An that specifies the culture-specific rules that will be used for formatting. + /// If is null. + public StringFormatter(IFormatProvider formatProvider) + : this(CultureType.IFormatProvider) + { + this.formatProvider = formatProvider ?? throw new ArgumentNullException(nameof(formatProvider)); + } + internal StringFormatter(CultureType cultureType) { this.cultureType = cultureType; @@ -92,6 +113,7 @@ internal StringFormatter(CultureType cultureType) /// /// Gets the culture of the current instance. /// + [Obsolete("Store the CultureInfo in your subclass from the CultureInfo constructor. Note that .NET doesn't provide a reliable way to get from IFormatProvider > CultureInfo, so this will return the current cultue when using the IFormatProvider constructor.")] protected virtual CultureInfo Culture { get @@ -112,12 +134,38 @@ protected virtual CultureInfo Culture } } + /// + /// Gets the of the current instance. + /// + private IFormatProvider FormatProvider + { + get + { + switch (cultureType) + { + case CultureType.IFormatProvider: + return formatProvider!; + case CultureType.CustomCulture: + return culture!; + case CultureType.InvariantCulture: + return CultureInfo.InvariantCulture; + case CultureType.CurrentCulture: + return CultureInfo.CurrentCulture; + case CultureType.CurrentUICulture: + return CultureInfo.CurrentUICulture; + default: + return CultureInfo.CurrentCulture; + } + } + } + internal enum CultureType { CurrentCulture, CurrentUICulture, InvariantCulture, - CustomCulture + CustomCulture, + IFormatProvider } /// @@ -130,9 +178,9 @@ internal enum CultureType if (typeof(ICustomFormatter).Equals(formatType)) return this; if (typeof(NumberFormatInfo).Equals(formatType)) - return NumberFormatInfo.GetInstance(Culture); + return NumberFormatInfo.GetInstance(FormatProvider); if (typeof(DateTimeFormatInfo).Equals(formatType)) - return DateTimeFormatInfo.GetInstance(Culture); + return DateTimeFormatInfo.GetInstance(FormatProvider); return null; } @@ -230,10 +278,15 @@ internal void OnDeserializedMethod(System.Runtime.Serialization.StreamingContext { // We only need to deserialize custom cultures. Note that if it is not a built-in // culture, this will fail. - // J2N TODO: On newer .NET platforms, there is an overload that accepts a predefinedOnly parameter - // that when set to false allows retrieving made-up cultures. Need to investigate. if (cultureType == CultureType.CustomCulture) - this.culture = CultureInfo.GetCultureInfo(new string(this.cultureSymbol!)); + { + this.culture = CultureInfo.GetCultureInfo(new string(this.cultureSymbol!) +#if FEATURE_CULTUREINFO_PREDEFINEDONLY + , predefinedOnly: true // We only support predefined cultures for serialization. End users must provide their own serializable IFormatProvider implementation for other types. +#endif + ); + } + } #endif diff --git a/tests/J2N.Tests/Text/TestStringFormatter.cs b/tests/J2N.Tests/Text/TestStringFormatter.cs index dccc5fce..12d280d8 100644 --- a/tests/J2N.Tests/Text/TestStringFormatter.cs +++ b/tests/J2N.Tests/Text/TestStringFormatter.cs @@ -131,6 +131,16 @@ public void TestDecimalPlaces_Float() public void TestDecimalPlaces_Double() { assertEquals("22.0", string.Format(StringFormatter.InvariantCulture, "{0}", 22d)); + + assertEquals("22,0", string.Format(new StringFormatter(new CultureInfo("fr-FR")), "{0}", 22d)); + } + + [Test] + public void TestIFormatProvider_Double() + { + assertEquals("22.0", string.Format((IFormatProvider)StringFormatter.InvariantCulture, "{0}", 22d)); + + assertEquals("22,0", string.Format(new StringFormatter((IFormatProvider)new CultureInfo("fr-FR")), "{0}", 22d)); } [Test]