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