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