New test.
[mono.git] / mcs / class / corlib / System.Globalization / CompareInfo.cs
index 9cdee5fab7a070126ebf08fa8678bd0f684f7d1c..d6dca63c7e16c0ab910029a6195ae27ec1bf1101 100644 (file)
@@ -1,5 +1,5 @@
 //
-// System.Globalization.CompareInfo
+// System.Globalization.CompareInfo.cs
 //
 // Authors:
 //   Rodrigo Moya (rodrigo@ximian.com)
 //
 
 using System.Reflection;
+using System.Collections;
 using System.Runtime.Serialization;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Mono.Globalization.Unicode;
 
 namespace System.Globalization
 {
        [Serializable]
-       public class CompareInfo : IDeserializationCallback
-       {
+#if !MOONLIGHT
+       [ComVisible (true)]
+       public class CompareInfo : IDeserializationCallback {
+
                static readonly bool useManagedCollation =
                        Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
-                       != "yes";
+                       != "yes" && MSCompatUnicodeTable.IsReady;
 
                internal static bool UseManagedCollation {
-                       get { return useManagedCollation && MSCompatUnicodeTable.IsReady; }
+                       get { return useManagedCollation; }
                }
 
+               void IDeserializationCallback.OnDeserialization(object sender)
+               {
+                       if (UseManagedCollation) {
+                               collator = new SimpleCollator (new CultureInfo (culture));
+                       } else {
+                               /* This will build the ICU collator, and store
+                                * the pointer in ICU_collator
+                                */
+                               try {
+                                       this.construct_compareinfo (icu_name);
+                               } catch {
+                               //      ICU_collator=IntPtr.Zero;
+                               }
+                       }
+               }
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void construct_compareinfo (string locale);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void free_internal_collator ();
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern int internal_compare (string str1, int offset1,
+                                                    int length1, string str2,
+                                                    int offset2, int length2,
+                                                    CompareOptions options);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void assign_sortkey (object key, string source,
+                                                   CompareOptions options);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern int internal_index (string source, int sindex,
+                                                  int count, char value,
+                                                  CompareOptions options,
+                                                  bool first);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern int internal_index (string source, int sindex,
+                                                  int count, string value,
+                                                  CompareOptions options,
+                                                  bool first);
+
+#else
+       public class CompareInfo {
+               internal static bool UseManagedCollation {
+                       get { return true; }
+               }
+#endif
+               const CompareOptions ValidCompareOptions_NoStringSort =
+                       CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace |
+                       CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth |
+                       CompareOptions.OrdinalIgnoreCase | CompareOptions.Ordinal;
+
+               const CompareOptions ValidCompareOptions = ValidCompareOptions_NoStringSort | CompareOptions.StringSort;
+
                // Keep in synch with MonoCompareInfo in the runtime. 
                private int culture;
                [NonSerialized]
                private string icu_name;
-               [NonSerialized]
-               private IntPtr ICU_collator;
+//             [NonSerialized]
+//             private IntPtr ICU_collator;
+
+#pragma warning disable 169            
                private int win32LCID;  // Unused, but MS.NET serializes this
+               private string m_name; // Unused, but MS.NET serializes this
+#pragma warning restore 169
 
                [NonSerialized]
                SimpleCollator collator;
+
+               // Maps culture IDs to SimpleCollator objects
+               private static Hashtable collators;
+
+               [NonSerialized]
+               // Protects access to 'collators'
+               private static object monitor = new Object ();
                
                /* Hide the .ctor() */
                CompareInfo() {}
                
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern void construct_compareinfo (string locale);
-               
                internal CompareInfo (CultureInfo ci)
                {
                        this.culture = ci.LCID;
-                       if (UseManagedCollation) 
-                               collator = new SimpleCollator (ci);
-                       else {
+                       if (UseManagedCollation) {
+                               lock (monitor) {
+                                       if (collators == null)
+                                               collators = new Hashtable ();
+                                       collator = (SimpleCollator)collators [ci.LCID];
+                                       if (collator == null) {
+                                               collator = new SimpleCollator (ci);
+                                               collators [ci.LCID] = collator;
+                                       }
+                               }
+                       } else {
+#if !MOONLIGHT
                                this.icu_name = ci.IcuName;
                                this.construct_compareinfo (icu_name);
+#endif
                        }
                }
-               
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern void free_internal_collator ();
 
                ~CompareInfo ()
                {
+#if !MOONLIGHT
                        free_internal_collator ();
+#endif
                }
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern int internal_compare (string str1, int offset1,
-                                                    int length1, string str2,
-                                                    int offset2, int length2,
-                                                    CompareOptions options);
-
+#if !MOONLIGHT
                private int internal_compare_managed (string str1, int offset1,
                                                int length1, string str2,
                                                int offset2, int length2,
@@ -111,21 +184,35 @@ namespace System.Globalization
                                internal_compare (str1, offset1, length1,
                                str2, offset2, length2, options);
                }
-
+#else
+               private int internal_compare_switch (string str1, int offset1,
+                                               int length1, string str2,
+                                               int offset2, int length2,
+                                               CompareOptions options)
+               {
+                       return collator.Compare (str1, offset1, length1,
+                               str2, offset2, length2, options);
+               }
+#endif
                public virtual int Compare (string string1, string string2)
                {
-                       /* Short cut... */
-                       if(string1.Length == 0 && string2.Length == 0)
-                               return(0);
-
-                       return(internal_compare_switch (string1, 0, string1.Length,
-                                                string2, 0, string2.Length,
-                                                CompareOptions.None));
+                       return Compare (string1, string2, CompareOptions.None);
                }
 
                public virtual int Compare (string string1, string string2,
                                            CompareOptions options)
                {
+                       if ((options & ValidCompareOptions) != options)
+                               throw new ArgumentException ("options");
+
+                       if (string1 == null) {
+                               if (string2 == null)
+                                       return 0;
+                               return -1;
+                       }
+                       if (string2 == null)
+                               return 1;
+
                        /* Short cut... */
                        if(string1.Length == 0 && string2.Length == 0)
                                return(0);
@@ -138,38 +225,24 @@ namespace System.Globalization
                public virtual int Compare (string string1, int offset1,
                                            string string2, int offset2)
                {
-                       /* Not in the spec, but ms does these short
-                        * cuts before checking the offsets (breaking
-                        * the offset >= string length specified check
-                        * in the process...)
-                        */
-                       if ((string1.Length == 0 || offset1 == string1.Length) &&
-                               (string2.Length == 0 || offset2 == string2.Length))
-                               return(0);
-
-                       if(offset1 < 0 || offset2 < 0) {
-                               throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
-                       }
-                       
-                       if(offset1 > string1.Length) {
-                               throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
-                       }
-                       
-                       if(offset2 > string2.Length) {
-                               throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
-                       }
-                       
-                       return(internal_compare_switch (string1, offset1,
-                                                string1.Length-offset1,
-                                                string2, offset2,
-                                                string2.Length-offset2,
-                                                CompareOptions.None));
+                       return Compare (string1, offset1, string2, offset2, CompareOptions.None);
                }
 
                public virtual int Compare (string string1, int offset1,
                                            string string2, int offset2,
                                            CompareOptions options)
                {
+                       if ((options & ValidCompareOptions) != options)
+                               throw new ArgumentException ("options");
+
+                       if (string1 == null) {
+                               if (string2 == null)
+                                       return 0;
+                               return -1;
+                       }
+                       if (string2 == null)
+                               return 1;
+
                        /* Not in the spec, but ms does these short
                         * cuts before checking the offsets (breaking
                         * the offset >= string length specified check
@@ -194,7 +267,7 @@ namespace System.Globalization
                        return(internal_compare_switch (string1, offset1,
                                                 string1.Length-offset1,
                                                 string2, offset2,
-                                                string2.Length-offset1,
+                                                string2.Length-offset2,
                                                 options));
                }
 
@@ -202,51 +275,25 @@ namespace System.Globalization
                                            int length1, string string2,
                                            int offset2, int length2)
                {
-                       /* Not in the spec, but ms does these short
-                        * cuts before checking the offsets (breaking
-                        * the offset >= string length specified check
-                        * in the process...)
-                        */
-                       if((string1.Length == 0 ||
-                               offset1 == string1.Length ||
-                               length1 == 0) &&
-                               (string2.Length == 0 ||
-                               offset2 == string2.Length ||
-                               length2 == 0))
-                               return(0);
-
-                       if(offset1 < 0 || length1 < 0 ||
-                          offset2 < 0 || length2 < 0) {
-                               throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
-                       }
-                       
-                       if(offset1 > string1.Length) {
-                               throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
-                       }
-                       
-                       if(offset2 > string2.Length) {
-                               throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
-                       }
-                       
-                       if(length1 > string1.Length-offset1) {
-                               throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
-                       }
-                       
-                       if(length2 > string2.Length-offset2) {
-                               throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
-                       }
-                       
-                       return(internal_compare_switch (string1, offset1, length1,
-                                                string2, offset2, length2,
-                                                CompareOptions.None));
+                       return Compare (string1, offset1, length1, string2, offset2, length2, CompareOptions.None);
                }
 
-               [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
                public virtual int Compare (string string1, int offset1,
                                            int length1, string string2,
                                            int offset2, int length2,
                                            CompareOptions options)
                {
+                       if ((options & ValidCompareOptions) != options)
+                               throw new ArgumentException ("options");
+
+                       if (string1 == null) {
+                               if (string2 == null)
+                                       return 0;
+                               return -1;
+                       }
+                       if (string2 == null)
+                               return 1;
+
                        /* Not in the spec, but ms does these short
                         * cuts before checking the offsets (breaking
                         * the offset >= string length specified check
@@ -348,10 +395,6 @@ namespace System.Globalization
                {
                        return(LCID);
                }
-
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern void assign_sortkey (object key, string source,
-                                                   CompareOptions options);
                
                public virtual SortKey GetSortKey(string source)
                {
@@ -361,6 +404,12 @@ namespace System.Globalization
                public virtual SortKey GetSortKey(string source,
                                                  CompareOptions options)
                {
+                       switch (options) {
+                       case CompareOptions.Ordinal:
+                       case CompareOptions.OrdinalIgnoreCase:
+                               throw new ArgumentException ("Now allowed CompareOptions.", "options");
+                       }
+#if !MOONLIGHT
                        if (UseManagedCollation)
                                return collator.GetSortKey (source, options);
                        SortKey key=new SortKey (culture, source, options);
@@ -372,6 +421,9 @@ namespace System.Globalization
                        assign_sortkey (key, source, options);
                        
                        return(key);
+#else
+                       return collator.GetSortKey (source, options);
+#endif
                }
 
                public virtual int IndexOf (string source, char value)
@@ -446,12 +498,7 @@ namespace System.Globalization
                                        CompareOptions.None));
                }
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern int internal_index (string source, int sindex,
-                                                  int count, char value,
-                                                  CompareOptions options,
-                                                  bool first);
-
+#if !MOONLIGHT
                private int internal_index_managed (string s, int sindex,
                        int count, char c, CompareOptions opt,
                        bool first)
@@ -465,18 +512,25 @@ namespace System.Globalization
                        int count, char c, CompareOptions opt,
                        bool first)
                {
-                       return UseManagedCollation &&
-#if NET_2_0
-                               ((CompareOptions.Ordinal & opt) == 0 ||
-                               (CompareOptions.OrdinalIgnoreCase & opt) == 0) ?
-#else
-                               (CompareOptions.Ordinal & opt) == 0 ?
-#endif
+                       // - forward IndexOf() icall is much faster than
+                       //   manged version, so always use icall. However,
+                       //   it does not work for OrdinalIgnoreCase, so
+                       //   do not avoid managed collator for that option.
+                       return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
                                internal_index_managed (s, sindex, count, c, opt, first) :
                                internal_index (s, sindex, count, c, opt, first);
                }
+#else
+               private int internal_index_switch (string s, int sindex,
+                       int count, char c, CompareOptions opt,
+                       bool first)
+               {
+                       return first ?
+                               collator.IndexOf (s, c, sindex, count, opt) :
+                               collator.LastIndexOf (s, c, sindex, count, opt);
+               }
+#endif
 
-               [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
                public virtual int IndexOf (string source, char value,
                                            int startIndex, int count,
                                            CompareOptions options)
@@ -490,9 +544,8 @@ namespace System.Globalization
                        if(count<0 || (source.Length - startIndex) < count) {
                                throw new ArgumentOutOfRangeException ("count");
                        }
-                       if((options & CompareOptions.StringSort)!=0) {
-                               throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
-                       }
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
                        
                        if(count==0) {
                                return(-1);
@@ -514,12 +567,7 @@ namespace System.Globalization
                        }
                }
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern int internal_index (string source, int sindex,
-                                                  int count, string value,
-                                                  CompareOptions options,
-                                                  bool first);
-
+#if !MOONLIGHT
                private int internal_index_managed (string s1, int sindex,
                        int count, string s2, CompareOptions opt,
                        bool first)
@@ -533,18 +581,25 @@ namespace System.Globalization
                        int count, string s2, CompareOptions opt,
                        bool first)
                {
-                       return UseManagedCollation &&
-#if NET_2_0
-                               ((CompareOptions.Ordinal & opt) == 0 ||
-                               (CompareOptions.OrdinalIgnoreCase & opt) == 0) ?
-#else
-                               (CompareOptions.Ordinal & opt) == 0 ?
-#endif
+                       // - forward IndexOf() icall is much faster than
+                       //   manged version, so always use icall. However,
+                       //   it does not work for OrdinalIgnoreCase, so
+                       //   do not avoid managed collator for that option.
+                       return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
                                internal_index_managed (s1, sindex, count, s2, opt, first) :
                                internal_index (s1, sindex, count, s2, opt, first);
                }
+#else
+               private int internal_index_switch (string s1, int sindex,
+                       int count, string s2, CompareOptions opt,
+                       bool first)
+               {
+                       return first ?
+                               collator.IndexOf (s1, s2, sindex, count, opt) :
+                               collator.LastIndexOf (s1, s2, sindex, count, opt);
+               }
+#endif
 
-               [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
                public virtual int IndexOf (string source, string value,
                                            int startIndex, int count,
                                            CompareOptions options)
@@ -561,6 +616,11 @@ namespace System.Globalization
                        if(count<0 || (source.Length - startIndex) < count) {
                                throw new ArgumentOutOfRangeException ("count");
                        }
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
+                       if(value.Length==0) {
+                               return(0);
+                       }
                        if(count==0) {
                                return(-1);
                        }
@@ -584,6 +644,9 @@ namespace System.Globalization
                                throw new ArgumentNullException("prefix");
                        }
 
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
+
                        if (UseManagedCollation)
                                return collator.IsPrefix (source, prefix, options);
 
@@ -611,6 +674,9 @@ namespace System.Globalization
                                throw new ArgumentNullException("suffix");
                        }
 
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
+
                        if (UseManagedCollation)
                                return collator.IsSuffix (source, suffix, options);
 
@@ -698,7 +764,6 @@ namespace System.Globalization
                                            CompareOptions.None));
                }
 
-               [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
                public virtual int LastIndexOf(string source, char value,
                                               int startIndex, int count,
                                               CompareOptions options)
@@ -712,9 +777,8 @@ namespace System.Globalization
                        if(count < 0 || (startIndex - count) < -1) {
                                throw new ArgumentOutOfRangeException("count");
                        }
-                       if((options & CompareOptions.StringSort)!=0) {
-                               throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
-                       }
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
                        
                        if(count==0) {
                                return(-1);
@@ -752,6 +816,8 @@ namespace System.Globalization
                        if(count < 0 || (startIndex - count) < -1) {
                                throw new ArgumentOutOfRangeException("count");
                        }
+                       if ((options & ValidCompareOptions_NoStringSort) != options)
+                               throw new ArgumentException ("options");
                        if(count == 0) {
                                return(-1);
                        }
@@ -765,39 +831,23 @@ namespace System.Globalization
                                               value, options, false));
                }
 
-#if NET_2_0
-               public static bool IsSortable (char c)
+               [ComVisible (false)]
+               public static bool IsSortable (char ch)
                {
-                       return MSCompatUnicodeTable.IsSortable (c);
+                       return MSCompatUnicodeTable.IsSortable (ch);
                }
 
-               public static bool IsSortable (string s)
+               [ComVisible (false)]
+               public static bool IsSortable (string text)
                {
-                       return MSCompatUnicodeTable.IsSortable (s);
+                       return MSCompatUnicodeTable.IsSortable (text);
                }
-#endif
 
                public override string ToString()
                {
                        return("CompareInfo - "+culture);
                }
 
-               void IDeserializationCallback.OnDeserialization(object sender)
-               {
-                       if (UseManagedCollation) {
-                               collator = new SimpleCollator (new CultureInfo (culture));
-                       } else {
-                               /* This will build the ICU collator, and store
-                                * the pointer in ICU_collator
-                                */
-                               try {
-                                       this.construct_compareinfo (icu_name);
-                               } catch {
-                                       ICU_collator=IntPtr.Zero;
-                               }
-                       }
-               }
-
                /* LAMESPEC: not mentioned in the spec, but corcompare
                 * shows it.  Some documentation about what it does
                 * would be nice.
@@ -808,5 +858,10 @@ namespace System.Globalization
                                return(culture);
                        }
                }
+
+               [ComVisible (false)]
+               public virtual string Name {
+                       get { return icu_name; }
+               }
        }
 }