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