Port serialization implementation to Unix globalization types
authorStephen Toub <stoub@microsoft.com>
Sat, 23 Jul 2016 17:49:15 +0000 (10:49 -0700)
committerStephen Toub <stoub@microsoft.com>
Mon, 15 Aug 2016 00:15:48 +0000 (20:15 -0400)
These will need to be tested.  They are a straight port from the Windows implementation, but the implementation has diverged, and it's possible (likely) there are some differences I didn't catch.

36 files changed:
src/mscorlib/corefx/SR.cs
src/mscorlib/corefx/System/Globalization/Calendar.cs
src/mscorlib/corefx/System/Globalization/CalendarWeekRule.cs
src/mscorlib/corefx/System/Globalization/ChineseLunisolarCalendar.cs
src/mscorlib/corefx/System/Globalization/CompareInfo.Unix.cs
src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs
src/mscorlib/corefx/System/Globalization/CompareInfo.cs
src/mscorlib/corefx/System/Globalization/CultureInfo.cs
src/mscorlib/corefx/System/Globalization/CultureNotFoundException.cs
src/mscorlib/corefx/System/Globalization/DateTimeFormatInfo.cs
src/mscorlib/corefx/System/Globalization/DayLightTime.cs
src/mscorlib/corefx/System/Globalization/EastAsianLunisolarCalendar.cs
src/mscorlib/corefx/System/Globalization/GregorianCalendar.cs
src/mscorlib/corefx/System/Globalization/GregorianCalendarHelper.cs
src/mscorlib/corefx/System/Globalization/GregorianCalendarTypes.cs
src/mscorlib/corefx/System/Globalization/HebrewCalendar.cs
src/mscorlib/corefx/System/Globalization/HijriCalendar.cs
src/mscorlib/corefx/System/Globalization/JapaneseCalendar.cs
src/mscorlib/corefx/System/Globalization/JapaneseLunisolarCalendar.cs
src/mscorlib/corefx/System/Globalization/JulianCalendar.cs
src/mscorlib/corefx/System/Globalization/KoreanCalendar.cs
src/mscorlib/corefx/System/Globalization/KoreanLunisolarCalendar.cs
src/mscorlib/corefx/System/Globalization/NumberFormatInfo.cs
src/mscorlib/corefx/System/Globalization/PersianCalendar.cs
src/mscorlib/corefx/System/Globalization/RegionInfo.cs
src/mscorlib/corefx/System/Globalization/StringInfo.cs
src/mscorlib/corefx/System/Globalization/TaiwanCalendar.cs
src/mscorlib/corefx/System/Globalization/TaiwanLunisolarCalendar.cs
src/mscorlib/corefx/System/Globalization/TextElementEnumerator.cs
src/mscorlib/corefx/System/Globalization/TextInfo.Unix.cs
src/mscorlib/corefx/System/Globalization/TextInfo.Windows.cs
src/mscorlib/corefx/System/Globalization/TextInfo.cs
src/mscorlib/corefx/System/Globalization/ThaiBuddhistCalendar.cs
src/mscorlib/corefx/System/Globalization/UmAlQuraCalendar.cs
src/mscorlib/corefx/System/Globalization/UnicodeCategory.cs
src/vm/object.h

index f0dbb74..513bd9d 100644 (file)
@@ -234,6 +234,11 @@ namespace System.Globalization
             get { return Environment.GetResourceString("Overflow_TimeSpanTooLong"); }
         }
 
+        public static string Serialization_MemberOutOfRange
+        {
+            get { return Environment.GetResourceString("Serialization_MemberOutOfRange"); }
+        }
+
         public static string Format(string formatString, params object[] args)
         {
             return string.Format(CultureInfo.CurrentCulture, formatString, args);
index 861ecc7..95e5889 100644 (file)
@@ -6,6 +6,7 @@ using System;
 using System.Runtime.CompilerServices;
 using System.Globalization;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
 
 namespace System.Globalization
 {
@@ -29,7 +30,7 @@ namespace System.Globalization
     // The calculation of hour/minute/second is moved to Calendar from GregorianCalendar,
     // since most of the calendars (or all?) have the same way of calcuating hour/minute/second.
 
-
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public abstract class Calendar : ICloneable
     {
@@ -63,7 +64,7 @@ namespace System.Globalization
 
         private int m_currentEraValue = -1;
 
-
+        [OptionalField(VersionAdded = 2)]
         private bool m_isReadOnly = false;
 
 #if INSIDE_CLR
index 74a6594..490951e 100644 (file)
@@ -6,6 +6,7 @@ using System;
 
 namespace System.Globalization
 {
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public enum CalendarWeekRule
     {
index a64f20b..6fd6d1a 100644 (file)
@@ -19,7 +19,7 @@ namespace System.Globalization
     **      Gregorian              1901/02/19          2101/01/28
     **      ChineseLunisolar   1901/01/01          2100/12/29
     */
-
+    [Serializable]
     public class ChineseLunisolarCalendar : EastAsianLunisolarCalendar
     {
         //
index 9041fc1..da394ca 100644 (file)
@@ -12,14 +12,21 @@ namespace System.Globalization
     public partial class CompareInfo
     {
         [SecurityCritical]
-        private readonly Interop.GlobalizationInterop.SafeSortHandle m_sortHandle;
+        [NonSerialized]
+        private Interop.GlobalizationInterop.SafeSortHandle m_sortHandle;
 
-        private readonly bool m_isAsciiEqualityOrdinal;
+        [NonSerialized]
+        private bool m_isAsciiEqualityOrdinal;
 
         [SecuritySafeCritical]
         internal CompareInfo(CultureInfo culture)
         {
             m_name = culture.m_name;
+            InitSort(culture);
+        }
+
+        private void InitSort(CultureInfo culture)
+        {
             m_sortName = culture.SortName;
             m_sortHandle = Interop.GlobalizationInterop.GetSortHandle(GetNullTerminatedUtf8String(m_sortName));
             m_isAsciiEqualityOrdinal = (m_sortName == "en-US" || m_sortName == "");
index 0f30ac3..557d427 100644 (file)
@@ -10,11 +10,15 @@ namespace System.Globalization
     {      
         internal unsafe CompareInfo(CultureInfo culture)
         {
-            const uint LCMAP_SORTHANDLE = 0x20000000;
-
             this.m_name = culture.m_name;
+            InitSort(culture);
+        }
+
+        private void InitSort(CultureInfo culture)
+        {
             this.m_sortName = culture.SortName;
 
+            const uint LCMAP_SORTHANDLE = 0x20000000;
             long handle;
             int ret = Interop.mincore.LCMapStringEx(m_sortName, LCMAP_SORTHANDLE, null, 0, (IntPtr)(&handle), IntPtr.Size, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
 
@@ -192,6 +196,7 @@ namespace System.Globalization
         }
 
         // PAL ends here
+        [NonSerialized]
         private readonly IntPtr _sortHandle;
 
         private const uint LCMAP_HASH = 0x00040000;
index 6bc54eb..9cfa16b 100644 (file)
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Diagnostics.Contracts;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
 using System.Threading;
-using System.Diagnostics.Contracts;
 
 namespace System.Globalization
 {
     [Flags]
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public enum CompareOptions
     {
@@ -39,8 +41,9 @@ namespace System.Globalization
         Ordinal = 0x40000000,   // This flag can not be used with other flags.
     }
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
-    public partial class CompareInfo
+    public partial class CompareInfo : IDeserializationCallback
     {
         // Mask used to check if IndexOf()/LastIndexOf()/IsPrefix()/IsPostfix() has the right flags.
         private const CompareOptions ValidIndexMaskOffFlags =
@@ -63,8 +66,10 @@ namespace System.Globalization
         // The interesting part is that since haw-US doesn't have its own sort, it has to point at another
         // locale, which is what SCOMPAREINFO does.
 
-        private readonly String m_name;  // The name used to construct this CompareInfo
-        private readonly String m_sortName; // The name that defines our behavior
+        [OptionalField(VersionAdded = 2)]
+        private String m_name;  // The name used to construct this CompareInfo
+        [NonSerialized] 
+        private String m_sortName; // The name that defines our behavior
 
         /*=================================GetCompareInfo==========================
         **Action: Get the CompareInfo for the specified culture.
@@ -86,6 +91,33 @@ namespace System.Globalization
             return CultureInfo.GetCultureInfo(name).CompareInfo;
         }
 
+        [OnDeserializing]
+        private void OnDeserializing(StreamingContext ctx)
+        {
+            m_name = null;
+        }
+
+        void IDeserializationCallback.OnDeserialization(Object sender)
+        {
+            OnDeserialized();
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            OnDeserialized();
+        }
+
+        private void OnDeserialized()
+        {
+            if (m_name != null)
+            {
+                InitSort(CultureInfo.GetCultureInfo(m_name));
+            }
+        }
+
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx) { }
 
         ///////////////////////////----- Name -----/////////////////////////////////
         //
index 611ffde..2e7b1c6 100644 (file)
 ////////////////////////////////////////////////////////////////////////////
 
 using System;
-using System.Security;
-using System.Threading;
 using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
 using System.Runtime;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
-using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Threading;
 
 namespace System.Globalization
 {
@@ -47,6 +48,7 @@ namespace System.Globalization
     using StringCultureInfoDictionary = LowLevelDictionary<string, CultureInfo>;
 #endif
 
+    [Serializable]
     public partial class CultureInfo : IFormatProvider, ICloneable
     {
         //--------------------------------------------------------------------//
@@ -72,8 +74,10 @@ namespace System.Globalization
         // For supported culture, this will be the CultureData instance that read data from mscorlib assembly.
         // For customized culture, this will be the CultureData instance that read data from user customized culture binary file.
         //
+        [NonSerialized]
         internal CultureData m_cultureData;
 
+        [NonSerialized]
         internal bool m_isInherited;
 
         // Names are confusing.  Here are 3 names we have:
@@ -94,11 +98,13 @@ namespace System.Globalization
 
         // This will hold the non sorting name to be returned from CultureInfo.Name property.
         // This has a de-DE style name even for de-DE_phoneb type cultures
+        [NonSerialized]
         private string m_nonSortName;
 
         // This will hold the sorting name to be returned from CultureInfo.SortName property.
         // This might be completely unrelated to the culture name if a custom culture.  Ie en-US for fj-FJ.
         // Otherwise its the sort name, ie: de-DE or de-DE_phoneb
+        [NonSerialized]
         private string m_sortName;
 
 
@@ -131,6 +137,7 @@ namespace System.Globalization
         private static volatile StringCultureInfoDictionary s_NameCachedCultures;
 
         //The parent culture.
+        [NonSerialized]
         private CultureInfo m_parent;
 
         static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture; 
@@ -186,18 +193,23 @@ namespace System.Globalization
                     SR.ArgumentNull_String);
             }
 
+            InitializeFromName(name, useUserOverride);
+        }
+
+        private void InitializeFromName(string name, bool useUserOverride)
+        {
             // Get our data providing record
             this.m_cultureData = CultureData.GetCultureData(name, useUserOverride);
 
             if (this.m_cultureData == null)
-                throw new CultureNotFoundException(
-                    "name", name, SR.Argument_CultureNotSupported);
+            {
+                throw new CultureNotFoundException("name", name, SR.Argument_CultureNotSupported);
+            }
 
             this.m_name = this.m_cultureData.CultureName;
             this.m_isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
         }
 
-
         // We do this to try to return the system UI language and the default user languages
         // This method will fallback if this fails (like Invariant)
         //
@@ -266,6 +278,23 @@ namespace System.Globalization
             return VerifyCultureName(culture.Name, throwException);
         }
 
+        // We need to store the override from the culture data record.
+        private bool m_useUserOverride;
+
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx)
+        {
+            m_name = m_cultureData.CultureName;
+            m_useUserOverride = m_cultureData.UseUserOverride;
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            Contract.Assert(m_name != null, "[CultureInfo.OnDeserialized] m_name != null");
+            InitializeFromName(m_name, m_useUserOverride);
+        }
+
         ////////////////////////////////////////////////////////////////////////
         //
         //  CurrentCulture
index ee7feef..bd59efb 100644 (file)
@@ -8,11 +8,13 @@
 
 using System;
 using System.Threading;
+using System.Runtime.Serialization;
 
 namespace System.Globalization
 {
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
-    public class CultureNotFoundException : ArgumentException
+    public class CultureNotFoundException : ArgumentException, ISerializable
     {
         private string m_invalidCultureName; // unrecognized culture name
 
@@ -48,6 +50,22 @@ namespace System.Globalization
             m_invalidCultureName = invalidCultureName;
         }
 
+        protected CultureNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
+        {
+            m_invalidCultureName = (string)info.GetValue("InvalidCultureName", typeof(string));
+        }
+
+        [System.Security.SecurityCritical]  // auto-generated_required
+        public override void GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (info == null)
+            {
+                throw new ArgumentNullException("info");
+            }
+
+            base.GetObjectData(info, context);
+            info.AddValue("InvalidCultureName", m_invalidCultureName, typeof(string));
+        }
 
         public virtual string InvalidCultureName
         {
index e4f4f85..d50ca0c 100644 (file)
@@ -3,13 +3,14 @@
 // See the LICENSE file in the project root for more information.
 
 using System;
-using System.Security;
-using System.Threading;
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics.Contracts;
 using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Security;
 using System.Text;
+using System.Threading;
 
 namespace System.Globalization
 {
@@ -53,7 +54,7 @@ namespace System.Globalization
     }
 
 
-
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public sealed class DateTimeFormatInfo : IFormatProvider, ICloneable
     {
@@ -62,19 +63,24 @@ namespace System.Globalization
         private static volatile DateTimeFormatInfo invariantInfo;
 
         // an index which points to a record in Culture Data Table.
+        [NonSerialized]
         private CultureData m_cultureData;
 
         // The culture name used to create this DTFI.
 
+        [OptionalField(VersionAdded = 2)]
         internal String m_name = null;
 
         // The language name of the culture used to create this DTFI.
+        [NonSerialized]
         private String m_langName = null;
 
         // CompareInfo usually used by the parser.
+        [NonSerialized]
         private CompareInfo m_compareInfo = null;
 
         // Culture matches current DTFI. mainly used for string comparisons during parsing.
+        [NonSerialized]
         private CultureInfo m_cultureInfo = null;
 
         //
@@ -148,6 +154,7 @@ namespace System.Globalization
         internal String longTimePattern = null;
         internal String shortTimePattern = null;
 
+        [OptionalField(VersionAdded = 3)]
         private String[] allYearMonthPatterns = null;
         internal String[] allShortDatePatterns = null;
         internal String[] allLongDatePatterns = null;
@@ -357,11 +364,73 @@ namespace System.Globalization
             Contract.Assert(this.allYearMonthPatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some year month patterns");
         }
 
+        [OptionalField(VersionAdded = 1)]
+        private bool m_useUserOverride;
+
         // This was synthesized by Whidbey so we knew what words might appear in the middle of a date string
         // Now we always synthesize so its not helpful
 
         internal String[] m_dateWords = null;
 
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx)
+        {
+            m_name = this.CultureName; // make sure the m_name is initialized.
+            m_useUserOverride = this.m_cultureData.UseUserOverride;
+
+            // Important to initialize these fields otherwise we may run into exception when deserializing on Whidbey
+            // because Whidbey try to initialize some of these fields using calendar data which could be null values 
+            // and then we get exceptions.  So we call the accessors to force the caches to get loaded.
+            Object o;
+            o = this.LongTimePattern;
+            o = this.LongDatePattern;
+            o = this.ShortTimePattern;
+            o = this.ShortDatePattern;
+            o = this.YearMonthPattern;
+            o = this.AllLongTimePatterns;
+            o = this.AllLongDatePatterns;
+            o = this.AllShortTimePatterns;
+            o = this.AllShortDatePatterns;
+            o = this.AllYearMonthPatterns;
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            if (m_name != null)
+            {
+                m_cultureData = CultureData.GetCultureData(m_name, m_useUserOverride);
+                if (m_cultureData == null)
+                {
+                    throw new CultureNotFoundException("m_name", m_name, SR.Argument_CultureNotSupported);
+                }
+            }
+
+            if (calendar == null)
+            {
+                calendar = (Calendar)GregorianCalendar.GetDefaultInstance().Clone();
+                calendar.SetReadOnlyState(m_isReadOnly);
+            }
+
+            InitializeOverridableProperties(m_cultureData, calendar.ID);
+
+            //
+            //  turn off read only state till we finish initializing all fields and then store read only state after we are done.
+            //
+            bool isReadOnly = m_isReadOnly;
+            m_isReadOnly = false;
+
+            // If we deserialized defaults ala Whidbey, make sure they're still defaults
+            // Whidbey's arrays could get a bit mixed up.
+            if (longDatePattern != null) this.LongDatePattern = longDatePattern;
+            if (shortDatePattern != null) this.ShortDatePattern = shortDatePattern;
+            if (yearMonthPattern != null) this.YearMonthPattern = yearMonthPattern;
+            if (longTimePattern != null) this.LongTimePattern = longTimePattern;
+            if (shortTimePattern != null) this.ShortTimePattern = shortTimePattern;
+
+            m_isReadOnly = isReadOnly;
+        }
+
         // Returns a default DateTimeFormatInfo that will be universally
         // supported and constant irrespective of the current culture.
         // Used by FromString methods.
index d16f061..28758c5 100644 (file)
@@ -8,7 +8,7 @@ namespace System.Globalization
 {
     // This class represents a starting/ending time for a period of daylight saving time.
 
-
+    [Serializable]
     internal class DaylightTime
     {
         internal DateTime m_start;
index 95b0b80..660299a 100644 (file)
@@ -13,7 +13,7 @@ namespace System.Globalization
     //
     ////////////////////////////////////////////////////////////////////////////
 
-
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public abstract class EastAsianLunisolarCalendar : Calendar
     {
index 5a4f156..d0933a0 100644 (file)
@@ -6,10 +6,11 @@
 
 //
 
-using System.Threading;
 using System;
 using System.Globalization;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+using System.Threading;
 
 namespace System.Globalization
 {
@@ -17,6 +18,7 @@ namespace System.Globalization
     // 0 CurrentEra (AD)
     // 1 BeforeCurrentEra (BC)
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
 
     public class GregorianCalendar : Calendar
@@ -53,6 +55,16 @@ namespace System.Globalization
 
         private static volatile Calendar s_defaultInstance;
 
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            if (m_type < GregorianCalendarTypes.Localized ||
+                m_type > GregorianCalendarTypes.TransliteratedFrench)
+            {
+                throw new SerializationException(
+                    String.Format(CultureInfo.CurrentCulture, SR.Serialization_MemberOutOfRange, "type", "GregorianCalendar"));
+            }
+        }
 
         [System.Runtime.InteropServices.ComVisible(false)]
         public override DateTime MinSupportedDateTime
index 27bdfa7..165272b 100644 (file)
@@ -3,12 +3,14 @@
 // See the LICENSE file in the project root for more information.
 
 using System;
-using System.Threading;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+using System.Threading;
 
 namespace System.Globalization
 {
     // Gregorian Calendars use Era Info
+    [Serializable]
     internal class EraInfo
     {
         internal int era;          // The value of the era.
@@ -20,8 +22,11 @@ namespace System.Globalization
                                    // be affected by the DateTime.MinValue;
         internal int maxEraYear;   // Max year value in this era. (== the year length of the era + 1)
 
+        [OptionalField(VersionAdded = 4)]
         internal String eraName;    // The era name
+        [OptionalField(VersionAdded = 4)]
         internal String abbrevEraName;  // Abbreviated Era Name
+        [OptionalField(VersionAdded = 4)]
         internal String englishEraName; // English era name
 
         internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearOffset, int minEraYear, int maxEraYear)
@@ -50,6 +55,7 @@ namespace System.Globalization
     // This calendar recognizes two era values:
     // 0 CurrentEra (AD) 
     // 1 BeforeCurrentEra (BC) 
+    [Serializable]
     internal class GregorianCalendarHelper
     {
         // 1 tick = 100ns = 10E-7 second
@@ -107,11 +113,15 @@ namespace System.Globalization
             0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
         };
 
+        [OptionalField(VersionAdded = 1)]
         internal int m_maxYear = 9999;
+        [OptionalField(VersionAdded = 1)]
         internal int m_minYear;
         internal Calendar m_Cal;
 
+        [OptionalField(VersionAdded = 1)]
         internal EraInfo[] m_EraInfo;
+        [OptionalField(VersionAdded = 1)]
         internal int[] m_eras = null;
 
 
index c60c124..a14010f 100644 (file)
@@ -8,6 +8,7 @@ namespace System.Globalization
 {
     // Note: The values of the members of this enum must match the coresponding values
     // in the CalendarId enum (since we cast between GregorianCalendarTypes and CalendarId).
+    [Serializable]
     public enum GregorianCalendarTypes
     {
         Localized = CalendarId.GREGORIAN,
index 2830d2f..a357d70 100644 (file)
@@ -67,6 +67,7 @@ namespace System.Globalization
     // Gregorian to Hebrew Lunar from 1583 to 2239.
 
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class HebrewCalendar : Calendar
     {
index bb13386..892116d 100644 (file)
@@ -44,6 +44,7 @@ namespace System.Globalization
     **      Hijri       0001/01/01   9666/04/03
     */
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public partial class HijriCalendar : Calendar
     {
index cc93592..5dee4f1 100644 (file)
@@ -43,6 +43,7 @@ namespace System.Globalization
     ============================================================================*/
 
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public partial class JapaneseCalendar : Calendar
     {
index 490c407..ee30e07 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Globalization
     **      JapaneseLunisolar      1960/01/01          2049/12/29
     */
 
+    [Serializable]
     public class JapaneseLunisolarCalendar : EastAsianLunisolarCalendar
     {
         //
index 48fdec5..a74a80c 100644 (file)
@@ -18,6 +18,7 @@ namespace System.Globalization
     //*      Gregorian   0001/01/01   9999/12/31
     //*      Julia       0001/01/03   9999/10/19
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class JulianCalendar : Calendar
     {
index 7f030aa..63f67ab 100644 (file)
@@ -24,6 +24,7 @@ namespace System.Globalization
     ============================================================================*/
 
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class KoreanCalendar : Calendar
     {
index a1f8a66..bbc03ee 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Globalization
     **      KoreanLunisolar    918/01/01          2050/13/29
     */
 
+    [Serializable]
     public class KoreanLunisolarCalendar : EastAsianLunisolarCalendar
     {
         //
index 859e342..1082b6f 100644 (file)
@@ -2,9 +2,10 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Text;
 using System;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+using System.Text;
 
 namespace System.Globalization
 {
@@ -40,6 +41,7 @@ namespace System.Globalization
     // CurrencySymbol            "$"      String used as local monetary symbol.
     //
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     sealed public class NumberFormatInfo : IFormatProvider, ICloneable
     {
@@ -70,6 +72,7 @@ namespace System.Globalization
         internal String perMilleSymbol = "\u2030";
 
 
+        [OptionalField(VersionAdded = 2)]
         internal String[] nativeDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
 
         internal int numberDecimalDigits = 2;
@@ -86,12 +89,21 @@ namespace System.Globalization
 
         // Is this NumberFormatInfo for invariant culture?
 
+        [OptionalField(VersionAdded = 2)]
         internal bool m_isInvariant = false;
 
         public NumberFormatInfo() : this(null)
         {
         }
 
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx) { }
+
+        [OnDeserializing]
+        private void OnDeserializing(StreamingContext ctx) { }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx) { }
 
         static private void VerifyDecimalSeparator(String decSep, String propertyName)
         {
index 15e4255..6e34f90 100644 (file)
@@ -24,6 +24,7 @@ namespace System.Globalization
      **      Persian     0001/01/01   9378/10/13
      */
 
+    [Serializable]
     public class PersianCalendar : Calendar
     {
         public static readonly int PersianEra = 1;
index 9df3ef8..6d1eef7 100644 (file)
 
 using System;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
 
 namespace System.Globalization
 {
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class RegionInfo
     {
@@ -104,6 +106,25 @@ namespace System.Globalization
             this.m_name = this.m_cultureData.SREGIONNAME;
         }
 
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx) { }
+
+        [System.Security.SecurityCritical]  // auto-generated
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            m_cultureData = CultureData.GetCultureData(m_name, true);
+
+            if (m_cultureData == null)
+            {
+                throw new ArgumentException(
+                    String.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidCultureName, m_name),
+                    "m_name");
+            }
+
+            m_name = this.m_cultureData.SREGIONNAME;
+        }
+
         ////////////////////////////////////////////////////////////////////////
         //
         //  GetCurrentRegion
index 8b19970..8c6b44a 100644 (file)
 
 using System;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
 
 namespace System.Globalization
 {
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class StringInfo
     {
+        [OptionalField(VersionAdded = 2)] 
         private String m_str;
 
+        [NonSerialized]
         private int[] m_indexes;
 
         // Legacy constructor
@@ -39,6 +43,21 @@ namespace System.Globalization
             this.String = value;
         }
 
+        [OnDeserializing]
+        private void OnDeserializing(StreamingContext ctx)
+        {
+            m_str = String.Empty;
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            if (m_str.Length == 0)
+            {
+                m_indexes = null;
+            }
+        }
+
         [System.Runtime.InteropServices.ComVisible(false)]
         public override bool Equals(Object value)
         {
index 52b60c9..e7f44df 100644 (file)
@@ -21,6 +21,7 @@ namespace System.Globalization
     **      Taiwan      01/01/01    8088/12/31
     ============================================================================*/
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class TaiwanCalendar : Calendar
     {
index 3a297c9..ebe601a 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Globalization
     **      TaiwanLunisolar     1912/01/01          2050/13/29
     */
 
+    [Serializable]
     public class TaiwanLunisolarCalendar : EastAsianLunisolarCalendar
     {
         // Since
index 475af8c..996aed9 100644 (file)
@@ -18,6 +18,7 @@
 
 using System.Collections;
 using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
 
 namespace System.Globalization
 {
@@ -25,6 +26,7 @@ namespace System.Globalization
     // This is public because GetTextElement() is public.
     //
 
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class TextElementEnumerator : IEnumerator
     {
@@ -32,12 +34,16 @@ namespace System.Globalization
         private int index;
         private int startIndex;
 
+        [NonSerialized] 
         private int strLen;                // This is the length of the total string, counting from the beginning of string.
 
+        [NonSerialized] 
         private int currTextElementLen; // The current text element lenght after MoveNext() is called.
 
+        [OptionalField(VersionAdded = 2)] 
         private UnicodeCategory uc;
 
+        [OptionalField(VersionAdded = 2)] 
         private int charLen;            // The next abstract char to look at after MoveNext() is called.  It could be 1 or 2, depending on if it is a surrogate or not.
 
         internal TextElementEnumerator(String str, int startIndex, int strLen)
@@ -51,6 +57,36 @@ namespace System.Globalization
             Reset();
         }
 
+        // the following fields is defined to keep the compatibility with Everett.
+        // don't change/remove the names/types of these fields.
+        private int endIndex;
+        private int nextTextElementLen;
+
+        [OnDeserializing]
+        private void OnDeserializing(StreamingContext ctx)
+        {
+            charLen = -1;
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            strLen = endIndex + 1;
+            currTextElementLen = nextTextElementLen;
+
+            if (charLen == -1)
+            {
+                uc = CharUnicodeInfo.InternalGetUnicodeCategory(str, index, out charLen);
+            }
+        }
+
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx)
+        {
+            endIndex = strLen - 1;
+            nextTextElementLen = currTextElementLen;
+        }
+
         public bool MoveNext()
         {
             if (index >= strLen)
index 8655cf4..4681170 100644 (file)
@@ -10,7 +10,7 @@ namespace System.Globalization
 {
     public partial class TextInfo
     {
-        private readonly bool m_needsTurkishCasing;
+        private bool m_needsTurkishCasing;
 
         //////////////////////////////////////////////////////////////////////////
         ////
@@ -24,7 +24,12 @@ namespace System.Globalization
             m_cultureData = cultureData;
             m_cultureName = m_cultureData.CultureName;
             m_textInfoName = m_cultureData.STEXTINFO;
-            m_needsTurkishCasing = NeedsTurkishCasing(m_textInfoName);
+            FinishInitialization(m_textInfoName);
+        }
+
+        private void FinishInitialization(string textInfoName)
+        {
+            m_needsTurkishCasing = NeedsTurkishCasing(textInfoName);
         }
 
         [SecuritySafeCritical]
index eaa68a7..bdaa35b 100644 (file)
@@ -16,15 +16,19 @@ namespace System.Globalization
         //////////////////////////////////////////////////////////////////////////
         internal unsafe TextInfo(CultureData cultureData)
         {
-            const uint LCMAP_SORTHANDLE = 0x20000000;
-
             // This is our primary data source, we don't need most of the rest of this
             this.m_cultureData = cultureData;
             this.m_cultureName = this.m_cultureData.CultureName;
             this.m_textInfoName = this.m_cultureData.STEXTINFO;
+            FinishInitialization(this.m_textInfoName);
+        }
+
+        private void FinishInitialization(string textInfoName)
+        {
+            const uint LCMAP_SORTHANDLE = 0x20000000;
 
             long handle;
-            int ret = Interop.mincore.LCMapStringEx(m_textInfoName, LCMAP_SORTHANDLE, null, 0, (IntPtr)(&handle), IntPtr.Size, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+            int ret = Interop.mincore.LCMapStringEx(textInfoName, LCMAP_SORTHANDLE, null, 0, (IntPtr)(&handle), IntPtr.Size, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
 
             _sortHandle = ret > 0 ? (IntPtr)handle : IntPtr.Zero;
         }
index 6e14499..da5ba51 100644 (file)
 //
 ////////////////////////////////////////////////////////////////////////////
 
-using System.Security;
 using System;
-using System.Text;
-using System.Threading;
-using System.Runtime;
 using System.Diagnostics.Contracts;
-using System.Runtime.InteropServices;
+using System.Runtime;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Text;
+using System.Threading;
 
 namespace System.Globalization
 {
-    public partial class TextInfo : ICloneable
+    [Serializable]
+    public partial class TextInfo : ICloneable, IDeserializationCallback
     {
         ////--------------------------------------------------------------------//
         ////                        Internal Information                        //
@@ -35,7 +37,9 @@ namespace System.Globalization
         ////  Variables.
         ////
 
+        [OptionalField(VersionAdded = 2)]
         private String m_listSeparator;
+        [OptionalField(VersionAdded = 2)]
         private bool m_isReadOnly = false;
 
         ////
@@ -58,9 +62,13 @@ namespace System.Globalization
         ////              know how to resolve custom locle names to sort ids so we have to have alredy resolved this.
         ////      
 
-        private readonly String m_cultureName;      // Name of the culture that created this text info
-        private readonly CultureData m_cultureData;      // Data record for the culture that made us, not for this textinfo
-        private readonly String m_textInfoName;     // Name of the text info we're using (ie: m_cultureData.STEXTINFO)
+        [OptionalField(VersionAdded = 3)]
+        private String m_cultureName;      // Name of the culture that created this text info
+        [NonSerialized]
+        private CultureData m_cultureData;      // Data record for the culture that made us, not for this textinfo
+        [NonSerialized]
+        private String m_textInfoName;     // Name of the text info we're using (ie: m_cultureData.STEXTINFO)
+        [NonSerialized]
         private bool? m_IsAsciiCasingSameAsInvariant;
 
         // Invariant text info
@@ -75,6 +83,40 @@ namespace System.Globalization
         }
         internal volatile static TextInfo s_Invariant;
 
+        [OnSerializing]
+        private void OnSerializing(StreamingContext ctx) { }
+
+        [OnDeserializing]
+        private void OnDeserializing(StreamingContext ctx)
+        {
+            // Clear these so we can check if we've fixed them yet            
+            this.m_cultureData = null;
+            this.m_cultureName = null;
+        }
+
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext ctx)
+        {
+            OnDeserialized();
+        }
+
+        void IDeserializationCallback.OnDeserialization(Object sender)
+        {
+            OnDeserialized();
+        }
+
+        private void OnDeserialized()
+        {
+            // this method will be called twice because of the support of IDeserializationCallback
+            if (this.m_cultureData == null)
+            {
+                // Get the text info name belonging to that culture
+                this.m_cultureData = CultureInfo.GetCultureInfo(m_cultureName).m_cultureData;
+                this.m_textInfoName = this.m_cultureData.STEXTINFO;
+                FinishInitialization(this.m_textInfoName);
+            }
+        }
+
         //
         // Internal ordinal comparison functions
         //
index 47d0108..026adfc 100644 (file)
@@ -20,7 +20,7 @@ namespace System.Globalization
     **      Thai        0544/01/01  10542/12/31
     ============================================================================*/
 
-
+    [Serializable]
     [System.Runtime.InteropServices.ComVisible(true)]
     public class ThaiBuddhistCalendar : Calendar
     {
index 8b46404..1dbcf65 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Globalization
     **      UmAlQura    1318/01/01   1500/12/30
     */
 
+    [Serializable]
     public class UmAlQuraCalendar : Calendar
     {
         internal const int MinCalendarYear = 1318;
index e9c959b..952f4f3 100644 (file)
@@ -18,6 +18,7 @@
 
 namespace System.Globalization
 {
+    [Serializable]
     public enum UnicodeCategory
     {
         UppercaseLetter = 0,
index 2d6b57d..1722c16 100644 (file)
@@ -1875,9 +1875,7 @@ private:
 #ifdef FEATURE_LEAK_CULTURE_INFO
     CLR_BOOL m_isSafeCrossDomain;
 #endif // FEATURE_LEAK_CULTURE_INFO
-#ifndef FEATURE_COREFX_GLOBALIZATION
     CLR_BOOL m_useUserOverride;
-#endif
 
 public:
     CULTUREINFOBASEREF GetParent()