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