2006-01-24 Chris Toshok <toshok@ximian.com>
[mono.git] / mcs / class / corlib / System.Globalization / CompareInfo.cs
index fa3e582414c0ecbbbdc40d63e30af6cb3ae957c1..3f589ca124d78c0598ecfccd80604783bc5af9c0 100644 (file)
 using System.Reflection;
 using System.Runtime.Serialization;
 using System.Runtime.CompilerServices;
+using Mono.Globalization.Unicode;
 
 namespace System.Globalization
 {
        [Serializable]
        public class CompareInfo : IDeserializationCallback
        {
+               static readonly bool useManagedCollation =
+                       Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
+                       != "yes" && MSCompatUnicodeTable.IsReady;
+
+               internal static bool UseManagedCollation {
+                       get { return useManagedCollation; }
+               }
+
                // Keep in synch with MonoCompareInfo in the runtime. 
                private int culture;
                [NonSerialized]
@@ -47,6 +56,12 @@ namespace System.Globalization
                [NonSerialized]
                private IntPtr ICU_collator;
                private int win32LCID;  // Unused, but MS.NET serializes this
+#if NET_2_0
+               private string m_name; // Unused, but MS.NET serializes this
+#endif
+
+               [NonSerialized]
+               SimpleCollator collator;
                
                /* Hide the .ctor() */
                CompareInfo() {}
@@ -57,39 +72,56 @@ namespace System.Globalization
                internal CompareInfo (CultureInfo ci)
                {
                        this.culture = ci.LCID;
-                       this.icu_name = ci.IcuName;
-                       this.construct_compareinfo (icu_name);
+                       if (UseManagedCollation) 
+                               collator = new SimpleCollator (ci);
+                       else {
+                               this.icu_name = ci.IcuName;
+                               this.construct_compareinfo (icu_name);
+                       }
                }
                
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern void free_internal_collator ();
-               
+
                ~CompareInfo ()
                {
                        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);
 
+               private int internal_compare_managed (string str1, int offset1,
+                                               int length1, string str2,
+                                               int offset2, int length2,
+                                               CompareOptions options)
+               {
+                       return collator.Compare (str1, offset1, length1,
+                               str2, offset2, length2, options);
+               }
+
+               private int internal_compare_switch (string str1, int offset1,
+                                               int length1, string str2,
+                                               int offset2, int length2,
+                                               CompareOptions options)
+               {
+                       return UseManagedCollation ?
+                               internal_compare_managed (str1, offset1, length1,
+                               str2, offset2, length2, options) :
+                               internal_compare (str1, offset1, length1,
+                               str2, offset2, length2, options);
+               }
+
                public virtual int Compare (string string1, string string2)
                {
-                       /* Short cuts... */
-                       if(string1.Length == 0) {
-                               if(string2.Length == 0) {
-                                       return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0) {
-                               return(1);
-                       }
+                       /* Short cut... */
+                       if(string1.Length == 0 && string2.Length == 0)
+                               return(0);
 
-                       return(internal_compare (string1, 0, string1.Length,
+                       return(internal_compare_switch (string1, 0, string1.Length,
                                                 string2, 0, string2.Length,
                                                 CompareOptions.None));
                }
@@ -97,18 +129,11 @@ namespace System.Globalization
                public virtual int Compare (string string1, string string2,
                                            CompareOptions options)
                {
-                       /* Short cuts... */
-                       if(string1.Length == 0) {
-                               if(string2.Length == 0) {
-                                       return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0) {
-                               return(1);
-                       }
+                       /* Short cut... */
+                       if(string1.Length == 0 && string2.Length == 0)
+                               return(0);
 
-                       return(internal_compare (string1, 0, string1.Length,
+                       return(internal_compare_switch (string1, 0, string1.Length,
                                                 string2, 0, string2.Length,
                                                 options));
                }
@@ -121,18 +146,9 @@ namespace System.Globalization
                         * the offset >= string length specified check
                         * in the process...)
                         */
-                       if(string1.Length == 0 ||
-                          offset1 == string1.Length) {
-                               if(string2.Length == 0 ||
-                                  offset2 == string2.Length) {
-                                       return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0 ||
-                                 offset2 == string2.Length) {
-                               return(1);
-                       }
+                       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");
@@ -146,7 +162,7 @@ namespace System.Globalization
                                throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
                        }
                        
-                       return(internal_compare (string1, offset1,
+                       return(internal_compare_switch (string1, offset1,
                                                 string1.Length-offset1,
                                                 string2, offset2,
                                                 string2.Length-offset2,
@@ -162,18 +178,9 @@ namespace System.Globalization
                         * the offset >= string length specified check
                         * in the process...)
                         */
-                       if(string1.Length == 0 ||
-                          offset1 == string1.Length) {
-                               if(string2.Length == 0 ||
-                                  offset2 == string2.Length) {
-                                       return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0 ||
-                                 offset2 == string2.Length) {
-                               return(1);
-                       }
+                       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");
@@ -187,7 +194,7 @@ namespace System.Globalization
                                throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
                        }
                        
-                       return(internal_compare (string1, offset1,
+                       return(internal_compare_switch (string1, offset1,
                                                 string1.Length-offset1,
                                                 string2, offset2,
                                                 string2.Length-offset1,
@@ -203,21 +210,13 @@ namespace System.Globalization
                         * the offset >= string length specified check
                         * in the process...)
                         */
-                       if(string1.Length == 0 ||
-                          offset1 == string1.Length ||
-                          length1 == 0) {
-                               if(string2.Length == 0 ||
-                                  offset2 == string2.Length ||
-                                  length2 == 0) {
-                                       return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0 ||
-                                 offset2 == string2.Length ||
-                                 length2 == 0) {
-                               return(1);
-                       }
+                       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) {
@@ -240,7 +239,7 @@ namespace System.Globalization
                                throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
                        }
                        
-                       return(internal_compare (string1, offset1, length1,
+                       return(internal_compare_switch (string1, offset1, length1,
                                                 string2, offset2, length2,
                                                 CompareOptions.None));
                }
@@ -255,21 +254,13 @@ namespace System.Globalization
                         * the offset >= string length specified check
                         * in the process...)
                         */
-                       if(string1.Length == 0 ||
-                          offset1 == string1.Length ||
-                          length1 == 0) {
-                               if(string2.Length == 0 ||
-                                  offset2 == string2.Length ||
-                                  length2 == 0) {
+                       if((string1.Length == 0 ||
+                               offset1 == string1.Length ||
+                               length1 == 0) &&
+                               (string2.Length == 0 ||
+                               offset2 == string2.Length ||
+                               length2 == 0))
                                        return(0);
-                               } else {
-                                       return(-1);
-                               }
-                       } else if(string2.Length == 0 ||
-                                 offset2 == string2.Length ||
-                                 length2 == 0) {
-                               return(1);
-                       }
 
                        if(offset1 < 0 || length1 < 0 ||
                           offset2 < 0 || length2 < 0) {
@@ -292,7 +283,7 @@ namespace System.Globalization
                                throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
                        }
                        
-                       return(internal_compare (string1, offset1, length1,
+                       return(internal_compare_switch (string1, offset1, length1,
                                                 string2, offset2, length2,
                                                 options));
                }
@@ -372,6 +363,15 @@ namespace System.Globalization
                public virtual SortKey GetSortKey(string source,
                                                  CompareOptions options)
                {
+#if NET_2_0
+                       switch (options) {
+                       case CompareOptions.Ordinal:
+                       case CompareOptions.OrdinalIgnoreCase:
+                               throw new ArgumentException ("Now allowed CompareOptions.", "options");
+                       }
+#endif
+                       if (UseManagedCollation)
+                               return collator.GetSortKey (source, options);
                        SortKey key=new SortKey (culture, source, options);
 
                        /* Need to do the icall here instead of in the
@@ -460,7 +460,29 @@ namespace System.Globalization
                                                   int count, char value,
                                                   CompareOptions options,
                                                   bool first);
-               
+
+               private int internal_index_managed (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);
+               }
+
+               private int internal_index_switch (string s, int sindex,
+                       int count, char c, CompareOptions opt,
+                       bool first)
+               {
+                       // - 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);
+               }
+
                public virtual int IndexOf (string source, char value,
                                            int startIndex, int count,
                                            CompareOptions options)
@@ -492,7 +514,7 @@ namespace System.Globalization
                                }
                                return(-1);
                        } else {
-                               return (internal_index (source, startIndex,
+                               return (internal_index_switch (source, startIndex,
                                                        count, value, options,
                                                        true));
                        }
@@ -503,7 +525,29 @@ namespace System.Globalization
                                                   int count, string value,
                                                   CompareOptions options,
                                                   bool first);
-               
+
+               private int internal_index_managed (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);
+               }
+
+               private int internal_index_switch (string s1, int sindex,
+                       int count, string s2, CompareOptions opt,
+                       bool first)
+               {
+                       // - 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);
+               }
+
                public virtual int IndexOf (string source, string value,
                                            int startIndex, int count,
                                            CompareOptions options)
@@ -524,7 +568,7 @@ namespace System.Globalization
                                return(-1);
                        }
 
-                       return (internal_index (source, startIndex, count,
+                       return (internal_index_switch (source, startIndex, count,
                                                value, options, true));
                }
 
@@ -543,6 +587,9 @@ namespace System.Globalization
                                throw new ArgumentNullException("prefix");
                        }
 
+                       if (UseManagedCollation)
+                               return collator.IsPrefix (source, prefix, options);
+
                        if(source.Length < prefix.Length) {
                                return(false);
                        } else {
@@ -567,6 +614,9 @@ namespace System.Globalization
                                throw new ArgumentNullException("suffix");
                        }
 
+                       if (UseManagedCollation)
+                               return collator.IsSuffix (source, suffix, options);
+
                        if(source.Length < suffix.Length) {
                                return(false);
                        } else {
@@ -682,7 +732,7 @@ namespace System.Globalization
                                }
                                return(-1);
                        } else {
-                               return (internal_index (source, startIndex,
+                               return (internal_index_switch (source, startIndex,
                                                        count, value, options,
                                                        false));
                        }
@@ -713,10 +763,22 @@ namespace System.Globalization
                                return(0);
                        }
 
-                       return(internal_index (source, startIndex, count,
+                       return(internal_index_switch (source, startIndex, count,
                                               value, options, false));
                }
 
+#if NET_2_0
+               public static bool IsSortable (char c)
+               {
+                       return MSCompatUnicodeTable.IsSortable (c);
+               }
+
+               public static bool IsSortable (string s)
+               {
+                       return MSCompatUnicodeTable.IsSortable (s);
+               }
+#endif
+
                public override string ToString()
                {
                        return("CompareInfo - "+culture);
@@ -724,13 +786,17 @@ namespace System.Globalization
 
                void IDeserializationCallback.OnDeserialization(object sender)
                {
-                       /* This will build the ICU collator, and store
-                        * the pointer in ICU_collator
-                        */
-                       try {
-                               this.construct_compareinfo (icu_name);
-                       } catch {
-                               ICU_collator=IntPtr.Zero;
+                       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;
+                               }
                        }
                }