Merge pull request #301 from directhex/master
[mono.git] / mcs / class / corlib / System.Globalization / CompareInfo.cs
1 //
2 // System.Globalization.CompareInfo.cs
3 //
4 // Authors:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Dick Porter (dick@ximian.com)
7 //
8 // (C) Ximian, Inc. 2002
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Reflection;
35 using System.Collections;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39 using Mono.Globalization.Unicode;
40
41 namespace System.Globalization
42 {
43         [Serializable]
44         [StructLayout (LayoutKind.Sequential)]
45 #if !MOONLIGHT
46         [ComVisible (true)]
47         public class CompareInfo : IDeserializationCallback {
48
49                 static readonly bool useManagedCollation =
50                         Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
51                         != "yes" && MSCompatUnicodeTable.IsReady;
52
53                 internal static bool UseManagedCollation {
54                         get { return useManagedCollation; }
55                 }
56
57                 void IDeserializationCallback.OnDeserialization(object sender)
58                 {
59                         if (UseManagedCollation) {
60                                 collator = new SimpleCollator (new CultureInfo (culture));
61                         } else {
62                                 /* This will build the ICU collator, and store
63                                  * the pointer in ICU_collator
64                                  */
65                                 /*
66                                 try {
67                                         this.construct_compareinfo (icu_name);
68                                 } catch {
69                                 //      ICU_collator=IntPtr.Zero;
70                                 }
71                                 */
72                         }
73                 }
74
75                 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
76                 //private extern void construct_compareinfo (string locale);
77
78                 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
79                 //private extern void free_internal_collator ();
80
81                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
82                 private extern int internal_compare (string str1, int offset1,
83                                                      int length1, string str2,
84                                                      int offset2, int length2,
85                                                      CompareOptions options);
86
87                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
88                 private extern void assign_sortkey (object key, string source,
89                                                     CompareOptions options);
90
91                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
92                 private extern int internal_index (string source, int sindex,
93                                                    int count, char value,
94                                                    CompareOptions options,
95                                                    bool first);
96
97                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
98                 private extern int internal_index (string source, int sindex,
99                                                    int count, string value,
100                                                    CompareOptions options,
101                                                    bool first);
102
103 #else
104         public class CompareInfo {
105                 internal static bool UseManagedCollation {
106                         get { return true; }
107                 }
108 #endif
109                 const CompareOptions ValidCompareOptions_NoStringSort =
110                         CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace |
111                         CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth |
112                         CompareOptions.OrdinalIgnoreCase | CompareOptions.Ordinal;
113
114                 const CompareOptions ValidCompareOptions = ValidCompareOptions_NoStringSort | CompareOptions.StringSort;
115
116                 // Keep in synch with MonoCompareInfo in the runtime. 
117                 private int culture;
118 //              [NonSerialized]
119 //              private string icu_name;
120 //              [NonSerialized]
121 //              private IntPtr ICU_collator;
122
123 #pragma warning disable 169             
124                 private int win32LCID;  // Unused, but MS.NET serializes this
125 #pragma warning restore 169
126                 
127                 readonly string m_name; // MS.NET serializes this
128
129                 [NonSerialized]
130                 SimpleCollator collator;
131
132                 // Maps culture IDs to SimpleCollator objects
133                 private static Hashtable collators;
134
135                 [NonSerialized]
136                 // Protects access to 'collators'
137                 private static object monitor = new Object ();
138                 
139                 internal CompareInfo (CultureInfo ci)
140                 {
141                         this.culture = ci.LCID;
142                         this.m_name = ci.Name;
143                         if (UseManagedCollation) {
144                                 lock (monitor) {
145                                         if (collators == null)
146                                                 collators = new Hashtable ();
147                                         collator = (SimpleCollator)collators [ci.LCID];
148                                         if (collator == null) {
149                                                 collator = new SimpleCollator (ci);
150                                                 collators [ci.LCID] = collator;
151                                         }
152                                 }
153                         } else {
154 /*
155 #if !MOONLIGHT
156                                 this.icu_name = ci.IcuName;
157                                 this.construct_compareinfo (icu_name);
158 #endif
159 */
160                         }
161                 }
162 /*
163                 ~CompareInfo ()
164                 {
165 #if !MOONLIGHT
166                         free_internal_collator ();
167 #endif
168                 }
169 */
170 #if !MOONLIGHT
171                 private int internal_compare_managed (string str1, int offset1,
172                                                 int length1, string str2,
173                                                 int offset2, int length2,
174                                                 CompareOptions options)
175                 {
176                         return collator.Compare (str1, offset1, length1,
177                                 str2, offset2, length2, options);
178                 }
179
180                 private int internal_compare_switch (string str1, int offset1,
181                                                 int length1, string str2,
182                                                 int offset2, int length2,
183                                                 CompareOptions options)
184                 {
185                         return UseManagedCollation ?
186                                 internal_compare_managed (str1, offset1, length1,
187                                 str2, offset2, length2, options) :
188                                 internal_compare (str1, offset1, length1,
189                                 str2, offset2, length2, options);
190                 }
191 #else
192                 private int internal_compare_switch (string str1, int offset1,
193                                                 int length1, string str2,
194                                                 int offset2, int length2,
195                                                 CompareOptions options)
196                 {
197                         return collator.Compare (str1, offset1, length1,
198                                 str2, offset2, length2, options);
199                 }
200 #endif
201                 public virtual int Compare (string string1, string string2)
202                 {
203                         return Compare (string1, string2, CompareOptions.None);
204                 }
205
206                 public virtual int Compare (string string1, string string2,
207                                             CompareOptions options)
208                 {
209                         if ((options & ValidCompareOptions) != options)
210                                 throw new ArgumentException ("options");
211
212                         if (string1 == null) {
213                                 if (string2 == null)
214                                         return 0;
215                                 return -1;
216                         }
217                         if (string2 == null)
218                                 return 1;
219
220                         /* Short cut... */
221                         if(string1.Length == 0 && string2.Length == 0)
222                                 return(0);
223
224                         return(internal_compare_switch (string1, 0, string1.Length,
225                                                  string2, 0, string2.Length,
226                                                  options));
227                 }
228
229                 public virtual int Compare (string string1, int offset1,
230                                             string string2, int offset2)
231                 {
232                         return Compare (string1, offset1, string2, offset2, CompareOptions.None);
233                 }
234
235                 public virtual int Compare (string string1, int offset1,
236                                             string string2, int offset2,
237                                             CompareOptions options)
238                 {
239                         if ((options & ValidCompareOptions) != options)
240                                 throw new ArgumentException ("options");
241
242                         if (string1 == null) {
243                                 if (string2 == null)
244                                         return 0;
245                                 return -1;
246                         }
247                         if (string2 == null)
248                                 return 1;
249
250                         /* Not in the spec, but ms does these short
251                          * cuts before checking the offsets (breaking
252                          * the offset >= string length specified check
253                          * in the process...)
254                          */
255                         if((string1.Length == 0 || offset1 == string1.Length) &&
256                                 (string2.Length == 0 || offset2 == string2.Length))
257                                 return(0);
258
259                         if(offset1 < 0 || offset2 < 0) {
260                                 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
261                         }
262                         
263                         if(offset1 > string1.Length) {
264                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
265                         }
266                         
267                         if(offset2 > string2.Length) {
268                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
269                         }
270                         
271                         return(internal_compare_switch (string1, offset1,
272                                                  string1.Length-offset1,
273                                                  string2, offset2,
274                                                  string2.Length-offset2,
275                                                  options));
276                 }
277
278                 public virtual int Compare (string string1, int offset1,
279                                             int length1, string string2,
280                                             int offset2, int length2)
281                 {
282                         return Compare (string1, offset1, length1, string2, offset2, length2, CompareOptions.None);
283                 }
284
285                 public virtual int Compare (string string1, int offset1,
286                                             int length1, string string2,
287                                             int offset2, int length2,
288                                             CompareOptions options)
289                 {
290                         if ((options & ValidCompareOptions) != options)
291                                 throw new ArgumentException ("options");
292
293                         if (string1 == null) {
294                                 if (string2 == null)
295                                         return 0;
296                                 return -1;
297                         }
298                         if (string2 == null)
299                                 return 1;
300
301                         /* Not in the spec, but ms does these short
302                          * cuts before checking the offsets (breaking
303                          * the offset >= string length specified check
304                          * in the process...)
305                          */
306                         if((string1.Length == 0 ||
307                                 offset1 == string1.Length ||
308                                 length1 == 0) &&
309                                 (string2.Length == 0 ||
310                                 offset2 == string2.Length ||
311                                 length2 == 0))
312                                         return(0);
313
314                         if(offset1 < 0 || length1 < 0 ||
315                            offset2 < 0 || length2 < 0) {
316                                 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
317                         }
318                         
319                         if(offset1 > string1.Length) {
320                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
321                         }
322                         
323                         if(offset2 > string2.Length) {
324                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
325                         }
326                         
327                         if(length1 > string1.Length-offset1) {
328                                 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
329                         }
330                         
331                         if(length2 > string2.Length-offset2) {
332                                 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
333                         }
334                         
335                         return(internal_compare_switch (string1, offset1, length1,
336                                                  string2, offset2, length2,
337                                                  options));
338                 }
339
340                 public override bool Equals(object value)
341                 {
342                         CompareInfo other=value as CompareInfo;
343                         if(other==null) {
344                                 return(false);
345                         }
346                         
347                         return(other.culture==culture);
348                 }
349
350                 public static CompareInfo GetCompareInfo(int culture)
351                 {
352                         return(new CultureInfo (culture).CompareInfo);
353                 }
354
355                 public static CompareInfo GetCompareInfo(string name)
356                 {
357                         if(name == null) {
358                                 throw new ArgumentNullException("name");
359                         }
360                         return(new CultureInfo (name).CompareInfo);
361                 }
362
363                 public static CompareInfo GetCompareInfo(int culture,
364                                                          Assembly assembly)
365                 {
366                         /* The assembly parameter is supposedly there
367                          * to allow some sort of compare algorithm
368                          * versioning.
369                          */
370                         if(assembly == null) {
371                                 throw new ArgumentNullException("assembly");
372                         }
373                         if(assembly!=typeof (Object).Module.Assembly) {
374                                 throw new ArgumentException ("Assembly is an invalid type");
375                         }
376                         return(GetCompareInfo (culture));
377                 }
378
379                 public static CompareInfo GetCompareInfo(string name,
380                                                          Assembly assembly)
381                 {
382                         /* The assembly parameter is supposedly there
383                          * to allow some sort of compare algorithm
384                          * versioning.
385                          */
386                         if(name == null) {
387                                 throw new ArgumentNullException("name");
388                         }
389                         if(assembly == null) {
390                                 throw new ArgumentNullException("assembly");
391                         }
392                         if(assembly!=typeof (Object).Module.Assembly) {
393                                 throw new ArgumentException ("Assembly is an invalid type");
394                         }
395                         return(GetCompareInfo (name));
396                 }
397
398                 public override int GetHashCode()
399                 {
400                         return(LCID);
401                 }
402                 
403                 public virtual SortKey GetSortKey(string source)
404                 {
405                         return(GetSortKey (source, CompareOptions.None));
406                 }
407
408                 public virtual SortKey GetSortKey(string source,
409                                                   CompareOptions options)
410                 {
411                         switch (options) {
412                         case CompareOptions.Ordinal:
413                         case CompareOptions.OrdinalIgnoreCase:
414                                 throw new ArgumentException ("Now allowed CompareOptions.", "options");
415                         }
416 #if !MOONLIGHT
417                         if (UseManagedCollation)
418                                 return collator.GetSortKey (source, options);
419                         SortKey key=new SortKey (culture, source, options);
420
421                         /* Need to do the icall here instead of in the
422                          * SortKey constructor, as we need access to
423                          * this instance's collator.
424                          */
425                         assign_sortkey (key, source, options);
426                         
427                         return(key);
428 #else
429                         return collator.GetSortKey (source, options);
430 #endif
431                 }
432
433                 public virtual int IndexOf (string source, char value)
434                 {
435                         return(IndexOf (source, value, 0, source.Length,
436                                         CompareOptions.None));
437                 }
438
439                 public virtual int IndexOf (string source, string value)
440                 {
441                         return(IndexOf (source, value, 0, source.Length,
442                                         CompareOptions.None));
443                 }
444
445                 public virtual int IndexOf (string source, char value,
446                                             CompareOptions options)
447                 {
448                         return(IndexOf (source, value, 0, source.Length,
449                                         options));
450                 }
451
452                 public virtual int IndexOf (string source, char value,
453                                             int startIndex)
454                 {
455                         return(IndexOf (source, value, startIndex,
456                                         source.Length - startIndex,
457                                         CompareOptions.None));
458                 }
459                 
460                 public virtual int IndexOf (string source, string value,
461                                             CompareOptions options)
462                 {
463                         return(IndexOf (source, value, 0, source.Length,
464                                         options));
465                 }
466
467                 public virtual int IndexOf (string source, string value,
468                                             int startIndex)
469                 {
470                         return(IndexOf (source, value, startIndex,
471                                         source.Length - startIndex,
472                                         CompareOptions.None));
473                 }
474
475                 public virtual int IndexOf (string source, char value,
476                                             int startIndex,
477                                             CompareOptions options)
478                 {
479                         return(IndexOf (source, value, startIndex,
480                                         source.Length - startIndex, options));
481                 }
482
483                 public virtual int IndexOf (string source, char value,
484                                             int startIndex, int count)
485                 {
486                         return IndexOf (source, value, startIndex, count,
487                                         CompareOptions.None);
488                 }
489
490                 public virtual int IndexOf (string source, string value,
491                                             int startIndex,
492                                             CompareOptions options)
493                 {
494                         return(IndexOf (source, value, startIndex,
495                                         source.Length - startIndex, options));
496                 }
497
498                 public virtual int IndexOf (string source, string value,
499                                             int startIndex, int count)
500                 {
501                         return(IndexOf (source, value, startIndex, count,
502                                         CompareOptions.None));
503                 }
504
505 #if !MOONLIGHT
506                 private int internal_index_managed (string s, int sindex,
507                         int count, char c, CompareOptions opt,
508                         bool first)
509                 {
510                         return first ?
511                                 collator.IndexOf (s, c, sindex, count, opt) :
512                                 collator.LastIndexOf (s, c, sindex, count, opt);
513                 }
514
515                 private int internal_index_switch (string s, int sindex,
516                         int count, char c, CompareOptions opt,
517                         bool first)
518                 {
519                         // - forward IndexOf() icall is much faster than
520                         //   manged version, so always use icall. However,
521                         //   it does not work for OrdinalIgnoreCase, so
522                         //   do not avoid managed collator for that option.
523                         return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
524                                 internal_index_managed (s, sindex, count, c, opt, first) :
525                                 internal_index (s, sindex, count, c, opt, first);
526                 }
527 #else
528                 private int internal_index_switch (string s, int sindex,
529                         int count, char c, CompareOptions opt,
530                         bool first)
531                 {
532                         return first ?
533                                 collator.IndexOf (s, c, sindex, count, opt) :
534                                 collator.LastIndexOf (s, c, sindex, count, opt);
535                 }
536 #endif
537
538                 public virtual int IndexOf (string source, char value,
539                                             int startIndex, int count,
540                                             CompareOptions options)
541                 {
542                         if(source==null) {
543                                 throw new ArgumentNullException ("source");
544                         }
545                         if(startIndex<0) {
546                                 throw new ArgumentOutOfRangeException ("startIndex");
547                         }
548                         if(count<0 || (source.Length - startIndex) < count) {
549                                 throw new ArgumentOutOfRangeException ("count");
550                         }
551                         if ((options & ValidCompareOptions_NoStringSort) != options)
552                                 throw new ArgumentException ("options");
553                         
554                         if(count==0) {
555                                 return(-1);
556                         }
557
558                         if((options & CompareOptions.Ordinal)!=0) {
559                                 for(int pos=startIndex;
560                                     pos < startIndex + count;
561                                     pos++) {
562                                         if(source[pos]==value) {
563                                                 return(pos);
564                                         }
565                                 }
566                                 return(-1);
567                         } else {
568                                 return (internal_index_switch (source, startIndex,
569                                                         count, value, options,
570                                                         true));
571                         }
572                 }
573
574 #if !MOONLIGHT
575                 private int internal_index_managed (string s1, int sindex,
576                         int count, string s2, CompareOptions opt,
577                         bool first)
578                 {
579                         return first ?
580                                 collator.IndexOf (s1, s2, sindex, count, opt) :
581                                 collator.LastIndexOf (s1, s2, sindex, count, opt);
582                 }
583
584                 private int internal_index_switch (string s1, int sindex,
585                         int count, string s2, CompareOptions opt,
586                         bool first)
587                 {
588                         // - forward IndexOf() icall is much faster than
589                         //   manged version, so always use icall. However,
590                         //   it does not work for OrdinalIgnoreCase, so
591                         //   do not avoid managed collator for that option.
592                         return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
593                                 internal_index_managed (s1, sindex, count, s2, opt, first) :
594                                 internal_index (s1, sindex, count, s2, opt, first);
595                 }
596 #else
597                 private int internal_index_switch (string s1, int sindex,
598                         int count, string s2, CompareOptions opt,
599                         bool first)
600                 {
601                         return first ?
602                                 collator.IndexOf (s1, s2, sindex, count, opt) :
603                                 collator.LastIndexOf (s1, s2, sindex, count, opt);
604                 }
605 #endif
606
607                 public virtual int IndexOf (string source, string value,
608                                             int startIndex, int count,
609                                             CompareOptions options)
610                 {
611                         if(source==null) {
612                                 throw new ArgumentNullException ("source");
613                         }
614                         if(value==null) {
615                                 throw new ArgumentNullException ("value");
616                         }
617                         if(startIndex<0) {
618                                 throw new ArgumentOutOfRangeException ("startIndex");
619                         }
620                         if(count<0 || (source.Length - startIndex) < count) {
621                                 throw new ArgumentOutOfRangeException ("count");
622                         }
623                         if ((options & ValidCompareOptions_NoStringSort) != options)
624                                 throw new ArgumentException ("options");
625                         if(value.Length==0) {
626                                 return(startIndex);
627                         }
628                         if(count==0) {
629                                 return(-1);
630                         }
631
632                         return (internal_index_switch (source, startIndex, count,
633                                                 value, options, true));
634                 }
635
636                 public virtual bool IsPrefix(string source, string prefix)
637                 {
638                         return(IsPrefix (source, prefix, CompareOptions.None));
639                 }
640
641                 public virtual bool IsPrefix(string source, string prefix,
642                                              CompareOptions options)
643                 {
644                         if(source == null) {
645                                 throw new ArgumentNullException("source");
646                         }
647                         if(prefix == null) {
648                                 throw new ArgumentNullException("prefix");
649                         }
650
651                         if ((options & ValidCompareOptions_NoStringSort) != options)
652                                 throw new ArgumentException ("options");
653
654                         if (UseManagedCollation)
655                                 return collator.IsPrefix (source, prefix, options);
656
657                         if(source.Length < prefix.Length) {
658                                 return(false);
659                         } else {
660                                 return(Compare (source, 0, prefix.Length,
661                                                 prefix, 0, prefix.Length,
662                                                 options)==0);
663                         }
664                 }
665
666                 public virtual bool IsSuffix(string source, string suffix)
667                 {
668                         return(IsSuffix (source, suffix, CompareOptions.None));
669                 }
670
671                 public virtual bool IsSuffix(string source, string suffix,
672                                              CompareOptions options)
673                 {
674                         if(source == null) {
675                                 throw new ArgumentNullException("source");
676                         }
677                         if(suffix == null) {
678                                 throw new ArgumentNullException("suffix");
679                         }
680
681                         if ((options & ValidCompareOptions_NoStringSort) != options)
682                                 throw new ArgumentException ("options");
683
684                         if (UseManagedCollation)
685                                 return collator.IsSuffix (source, suffix, options);
686
687                         if(source.Length < suffix.Length) {
688                                 return(false);
689                         } else {
690                                 return(Compare (source,
691                                                 source.Length - suffix.Length,
692                                                 suffix.Length, suffix, 0,
693                                                 suffix.Length, options)==0);
694                         }
695                 }
696
697                 public virtual int LastIndexOf(string source, char value)
698                 {
699                         return(LastIndexOf (source, value, source.Length - 1,
700                                             source.Length, CompareOptions.None));
701                 }
702
703                 public virtual int LastIndexOf(string source, string value)
704                 {
705                         return(LastIndexOf (source, value, source.Length - 1,
706                                             source.Length, CompareOptions.None));
707                 }
708
709                 public virtual int LastIndexOf(string source, char value,
710                                                CompareOptions options)
711                 {
712                         return(LastIndexOf (source, value, source.Length - 1,
713                                             source.Length, options));
714                 }
715
716                 public virtual int LastIndexOf(string source, char value,
717                                                int startIndex)
718                 {
719                         return(LastIndexOf (source, value, startIndex,
720                                             startIndex + 1,
721                                             CompareOptions.None));
722                 }
723
724                 public virtual int LastIndexOf(string source, string value,
725                                                CompareOptions options)
726                 {
727                         return(LastIndexOf (source, value, source.Length - 1,
728                                             source.Length, options));
729                 }
730
731                 public virtual int LastIndexOf(string source, string value,
732                                                int startIndex)
733                 {
734                         return(LastIndexOf (source, value, startIndex,
735                                             startIndex + 1,
736                                             CompareOptions.None));
737                 }
738
739                 public virtual int LastIndexOf(string source, char value,
740                                                int startIndex,
741                                                CompareOptions options)
742                 {
743                         return(LastIndexOf (source, value, startIndex,
744                                             startIndex + 1,
745                                             options));
746                 }
747
748                 public virtual int LastIndexOf(string source, char value,
749                                                int startIndex, int count)
750                 {
751                         return(LastIndexOf (source, value, startIndex, count,
752                                             CompareOptions.None));
753                 }
754
755                 public virtual int LastIndexOf(string source, string value,
756                                                int startIndex,
757                                                CompareOptions options)
758                 {
759                         return(LastIndexOf (source, value, startIndex,
760                                             startIndex + 1,
761                                             options));
762                 }
763
764                 public virtual int LastIndexOf(string source, string value,
765                                                int startIndex, int count)
766                 {
767                         return(LastIndexOf (source, value, startIndex, count,
768                                             CompareOptions.None));
769                 }
770
771                 public virtual int LastIndexOf(string source, char value,
772                                                int startIndex, int count,
773                                                CompareOptions options)
774                 {
775                         if(source == null) {
776                                 throw new ArgumentNullException("source");
777                         }
778                         if(startIndex < 0) {
779                                 throw new ArgumentOutOfRangeException ("startIndex");
780                         }
781                         if(count < 0 || (startIndex - count) < -1) {
782                                 throw new ArgumentOutOfRangeException("count");
783                         }
784                         if ((options & ValidCompareOptions_NoStringSort) != options)
785                                 throw new ArgumentException ("options");
786                         
787                         if(count==0) {
788                                 return(-1);
789                         }
790
791                         if((options & CompareOptions.Ordinal)!=0) {
792                                 for(int pos=startIndex;
793                                     pos > startIndex - count;
794                                     pos--) {
795                                         if(source[pos]==value) {
796                                                 return(pos);
797                                         }
798                                 }
799                                 return(-1);
800                         } else {
801                                 return (internal_index_switch (source, startIndex,
802                                                         count, value, options,
803                                                         false));
804                         }
805                 }
806
807                 public virtual int LastIndexOf(string source, string value,
808                                                int startIndex, int count,
809                                                CompareOptions options)
810                 {
811                         if(source == null) {
812                                 throw new ArgumentNullException("source");
813                         }
814                         if(value == null) {
815                                 throw new ArgumentNullException("value");
816                         }
817                         if(startIndex < 0) {
818                                 throw new ArgumentOutOfRangeException ("startIndex");
819                         }
820                         if(count < 0 || (startIndex - count) < -1) {
821                                 throw new ArgumentOutOfRangeException("count");
822                         }
823                         if ((options & ValidCompareOptions_NoStringSort) != options)
824                                 throw new ArgumentException ("options");
825                         if(count == 0) {
826                                 return(-1);
827                         }
828
829                         int valuelen=value.Length;
830                         if(valuelen==0) {
831                                 return(startIndex);
832                         }
833
834                         return(internal_index_switch (source, startIndex, count,
835                                                value, options, false));
836                 }
837
838                 [ComVisible (false)]
839                 public static bool IsSortable (char ch)
840                 {
841                         return MSCompatUnicodeTable.IsSortable (ch);
842                 }
843
844                 [ComVisible (false)]
845                 public static bool IsSortable (string text)
846                 {
847                         return MSCompatUnicodeTable.IsSortable (text);
848                 }
849
850                 public override string ToString()
851                 {
852                         return("CompareInfo - "+culture);
853                 }
854
855                 /* LAMESPEC: not mentioned in the spec, but corcompare
856                  * shows it.  Some documentation about what it does
857                  * would be nice.
858                  */
859                 public int LCID {
860                         get {
861                                 return culture;
862                         }
863                 }
864
865                 [ComVisible (false)]
866                 public virtual string Name {
867                         get { return m_name; }
868                 }
869         }
870 }