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