TimeSpan.TryParseExact should not try to parse null input.
[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                 public virtual SortKey GetSortKey(string source)
389                 {
390                         return(GetSortKey (source, CompareOptions.None));
391                 }
392
393                 public virtual SortKey GetSortKey(string source,
394                                                   CompareOptions options)
395                 {
396                         switch (options) {
397                         case CompareOptions.Ordinal:
398                         case CompareOptions.OrdinalIgnoreCase:
399                                 throw new ArgumentException ("Now allowed CompareOptions.", "options");
400                         }
401                         if (UseManagedCollation)
402                                 return collator.GetSortKey (source, options);
403                         SortKey key=new SortKey (culture, source, options);
404
405                         /* Need to do the icall here instead of in the
406                          * SortKey constructor, as we need access to
407                          * this instance's collator.
408                          */
409                         assign_sortkey (key, source, options);
410                         
411                         return(key);
412                 }
413
414                 public virtual int IndexOf (string source, char value)
415                 {
416                         return(IndexOf (source, value, 0, source.Length,
417                                         CompareOptions.None));
418                 }
419
420                 public virtual int IndexOf (string source, string value)
421                 {
422                         return(IndexOf (source, value, 0, source.Length,
423                                         CompareOptions.None));
424                 }
425
426                 public virtual int IndexOf (string source, char value,
427                                             CompareOptions options)
428                 {
429                         return(IndexOf (source, value, 0, source.Length,
430                                         options));
431                 }
432
433                 public virtual int IndexOf (string source, char value,
434                                             int startIndex)
435                 {
436                         return(IndexOf (source, value, startIndex,
437                                         source.Length - startIndex,
438                                         CompareOptions.None));
439                 }
440                 
441                 public virtual int IndexOf (string source, string value,
442                                             CompareOptions options)
443                 {
444                         return(IndexOf (source, value, 0, source.Length,
445                                         options));
446                 }
447
448                 public virtual int IndexOf (string source, string value,
449                                             int startIndex)
450                 {
451                         return(IndexOf (source, value, startIndex,
452                                         source.Length - startIndex,
453                                         CompareOptions.None));
454                 }
455
456                 public virtual int IndexOf (string source, char value,
457                                             int startIndex,
458                                             CompareOptions options)
459                 {
460                         return(IndexOf (source, value, startIndex,
461                                         source.Length - startIndex, options));
462                 }
463
464                 public virtual int IndexOf (string source, char value,
465                                             int startIndex, int count)
466                 {
467                         return IndexOf (source, value, startIndex, count,
468                                         CompareOptions.None);
469                 }
470
471                 public virtual int IndexOf (string source, string value,
472                                             int startIndex,
473                                             CompareOptions options)
474                 {
475                         return(IndexOf (source, value, startIndex,
476                                         source.Length - startIndex, options));
477                 }
478
479                 public virtual int IndexOf (string source, string value,
480                                             int startIndex, int count)
481                 {
482                         return(IndexOf (source, value, startIndex, count,
483                                         CompareOptions.None));
484                 }
485
486                 private int internal_index_managed (string s, int sindex,
487                         int count, char c, CompareOptions opt,
488                         bool first)
489                 {
490                         return first ?
491                                 collator.IndexOf (s, c, sindex, count, opt) :
492                                 collator.LastIndexOf (s, c, sindex, count, opt);
493                 }
494
495                 private int internal_index_switch (string s, int sindex,
496                         int count, char c, CompareOptions opt,
497                         bool first)
498                 {
499                         // - forward IndexOf() icall is much faster than
500                         //   manged version, so always use icall. However,
501                         //   it does not work for OrdinalIgnoreCase, so
502                         //   do not avoid managed collator for that option.
503                         return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
504                                 internal_index_managed (s, sindex, count, c, opt, first) :
505                                 internal_index (s, sindex, count, c, opt, first);
506                 }
507
508                 public virtual int IndexOf (string source, char value,
509                                             int startIndex, int count,
510                                             CompareOptions options)
511                 {
512                         if(source==null) {
513                                 throw new ArgumentNullException ("source");
514                         }
515                         if(startIndex<0) {
516                                 throw new ArgumentOutOfRangeException ("startIndex");
517                         }
518                         if(count<0 || (source.Length - startIndex) < count) {
519                                 throw new ArgumentOutOfRangeException ("count");
520                         }
521                         if ((options & ValidCompareOptions_NoStringSort) != options)
522                                 throw new ArgumentException ("options");
523                         
524                         if(count==0) {
525                                 return(-1);
526                         }
527
528                         if((options & CompareOptions.Ordinal)!=0) {
529                                 for(int pos=startIndex;
530                                     pos < startIndex + count;
531                                     pos++) {
532                                         if(source[pos]==value) {
533                                                 return(pos);
534                                         }
535                                 }
536                                 return(-1);
537                         } else {
538                                 return (internal_index_switch (source, startIndex,
539                                                         count, value, options,
540                                                         true));
541                         }
542                 }
543
544                 private int internal_index_managed (string s1, int sindex,
545                         int count, string s2, CompareOptions opt,
546                         bool first)
547                 {
548                         return first ?
549                                 collator.IndexOf (s1, s2, sindex, count, opt) :
550                                 collator.LastIndexOf (s1, s2, sindex, count, opt);
551                 }
552
553                 private int internal_index_switch (string s1, int sindex,
554                         int count, string s2, CompareOptions opt,
555                         bool first)
556                 {
557                         // - forward IndexOf() icall is much faster than
558                         //   manged version, so always use icall. However,
559                         //   it does not work for OrdinalIgnoreCase, so
560                         //   do not avoid managed collator for that option.
561                         return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
562                                 internal_index_managed (s1, sindex, count, s2, opt, first) :
563                                 internal_index (s1, sindex, count, s2, opt, first);
564                 }
565
566                 public virtual int IndexOf (string source, string value,
567                                             int startIndex, int count,
568                                             CompareOptions options)
569                 {
570                         if(source==null) {
571                                 throw new ArgumentNullException ("source");
572                         }
573                         if(value==null) {
574                                 throw new ArgumentNullException ("value");
575                         }
576                         if(startIndex<0) {
577                                 throw new ArgumentOutOfRangeException ("startIndex");
578                         }
579                         if(count<0 || (source.Length - startIndex) < count) {
580                                 throw new ArgumentOutOfRangeException ("count");
581                         }
582                         if ((options & ValidCompareOptions_NoStringSort) != options)
583                                 throw new ArgumentException ("options");
584                         if(value.Length==0) {
585                                 return(startIndex);
586                         }
587                         if(count==0) {
588                                 return(-1);
589                         }
590
591                         return (internal_index_switch (source, startIndex, count,
592                                                 value, options, true));
593                 }
594
595                 public virtual bool IsPrefix(string source, string prefix)
596                 {
597                         return(IsPrefix (source, prefix, CompareOptions.None));
598                 }
599
600                 public virtual bool IsPrefix(string source, string prefix,
601                                              CompareOptions options)
602                 {
603                         if(source == null) {
604                                 throw new ArgumentNullException("source");
605                         }
606                         if(prefix == null) {
607                                 throw new ArgumentNullException("prefix");
608                         }
609
610                         if ((options & ValidCompareOptions_NoStringSort) != options)
611                                 throw new ArgumentException ("options");
612
613                         if (UseManagedCollation)
614                                 return collator.IsPrefix (source, prefix, options);
615
616                         if(source.Length < prefix.Length) {
617                                 return(false);
618                         } else {
619                                 return(Compare (source, 0, prefix.Length,
620                                                 prefix, 0, prefix.Length,
621                                                 options)==0);
622                         }
623                 }
624
625                 public virtual bool IsSuffix(string source, string suffix)
626                 {
627                         return(IsSuffix (source, suffix, CompareOptions.None));
628                 }
629
630                 public virtual bool IsSuffix(string source, string suffix,
631                                              CompareOptions options)
632                 {
633                         if(source == null) {
634                                 throw new ArgumentNullException("source");
635                         }
636                         if(suffix == null) {
637                                 throw new ArgumentNullException("suffix");
638                         }
639
640                         if ((options & ValidCompareOptions_NoStringSort) != options)
641                                 throw new ArgumentException ("options");
642
643                         if (UseManagedCollation)
644                                 return collator.IsSuffix (source, suffix, options);
645
646                         if(source.Length < suffix.Length) {
647                                 return(false);
648                         } else {
649                                 return(Compare (source,
650                                                 source.Length - suffix.Length,
651                                                 suffix.Length, suffix, 0,
652                                                 suffix.Length, options)==0);
653                         }
654                 }
655
656                 public virtual int LastIndexOf(string source, char value)
657                 {
658                         return(LastIndexOf (source, value, source.Length - 1,
659                                             source.Length, CompareOptions.None));
660                 }
661
662                 public virtual int LastIndexOf(string source, string value)
663                 {
664                         return(LastIndexOf (source, value, source.Length - 1,
665                                             source.Length, CompareOptions.None));
666                 }
667
668                 public virtual int LastIndexOf(string source, char value,
669                                                CompareOptions options)
670                 {
671                         return(LastIndexOf (source, value, source.Length - 1,
672                                             source.Length, options));
673                 }
674
675                 public virtual int LastIndexOf(string source, char value,
676                                                int startIndex)
677                 {
678                         return(LastIndexOf (source, value, startIndex,
679                                             startIndex + 1,
680                                             CompareOptions.None));
681                 }
682
683                 public virtual int LastIndexOf(string source, string value,
684                                                CompareOptions options)
685                 {
686                         return(LastIndexOf (source, value, source.Length - 1,
687                                             source.Length, options));
688                 }
689
690                 public virtual int LastIndexOf(string source, string value,
691                                                int startIndex)
692                 {
693                         return(LastIndexOf (source, value, startIndex,
694                                             startIndex + 1,
695                                             CompareOptions.None));
696                 }
697
698                 public virtual int LastIndexOf(string source, char value,
699                                                int startIndex,
700                                                CompareOptions options)
701                 {
702                         return(LastIndexOf (source, value, startIndex,
703                                             startIndex + 1,
704                                             options));
705                 }
706
707                 public virtual int LastIndexOf(string source, char value,
708                                                int startIndex, int count)
709                 {
710                         return(LastIndexOf (source, value, startIndex, count,
711                                             CompareOptions.None));
712                 }
713
714                 public virtual int LastIndexOf(string source, string value,
715                                                int startIndex,
716                                                CompareOptions options)
717                 {
718                         return(LastIndexOf (source, value, startIndex,
719                                             startIndex + 1,
720                                             options));
721                 }
722
723                 public virtual int LastIndexOf(string source, string value,
724                                                int startIndex, int count)
725                 {
726                         return(LastIndexOf (source, value, startIndex, count,
727                                             CompareOptions.None));
728                 }
729
730                 public virtual int LastIndexOf(string source, char value,
731                                                int startIndex, int count,
732                                                CompareOptions options)
733                 {
734                         if(source == null) {
735                                 throw new ArgumentNullException("source");
736                         }
737                         if(startIndex < 0) {
738                                 throw new ArgumentOutOfRangeException ("startIndex");
739                         }
740                         if(count < 0 || (startIndex - count) < -1) {
741                                 throw new ArgumentOutOfRangeException("count");
742                         }
743                         if ((options & ValidCompareOptions_NoStringSort) != options)
744                                 throw new ArgumentException ("options");
745                         
746                         if(count==0) {
747                                 return(-1);
748                         }
749
750                         if((options & CompareOptions.Ordinal)!=0) {
751                                 for(int pos=startIndex;
752                                     pos > startIndex - count;
753                                     pos--) {
754                                         if(source[pos]==value) {
755                                                 return(pos);
756                                         }
757                                 }
758                                 return(-1);
759                         } else {
760                                 return (internal_index_switch (source, startIndex,
761                                                         count, value, options,
762                                                         false));
763                         }
764                 }
765
766                 public virtual int LastIndexOf(string source, string value,
767                                                int startIndex, int count,
768                                                CompareOptions options)
769                 {
770                         if(source == null) {
771                                 throw new ArgumentNullException("source");
772                         }
773                         if(value == null) {
774                                 throw new ArgumentNullException("value");
775                         }
776                         if(startIndex < 0) {
777                                 throw new ArgumentOutOfRangeException ("startIndex");
778                         }
779                         if(count < 0 || (startIndex - count) < -1) {
780                                 throw new ArgumentOutOfRangeException("count");
781                         }
782                         if ((options & ValidCompareOptions_NoStringSort) != options)
783                                 throw new ArgumentException ("options");
784                         if(count == 0) {
785                                 return(-1);
786                         }
787
788                         int valuelen=value.Length;
789                         if(valuelen==0) {
790                                 return(startIndex);
791                         }
792
793                         return(internal_index_switch (source, startIndex, count,
794                                                value, options, false));
795                 }
796
797                 [ComVisible (false)]
798                 public static bool IsSortable (char ch)
799                 {
800                         return MSCompatUnicodeTable.IsSortable (ch);
801                 }
802
803                 [ComVisible (false)]
804                 public static bool IsSortable (string text)
805                 {
806                         return MSCompatUnicodeTable.IsSortable (text);
807                 }
808
809                 public override string ToString()
810                 {
811                         return("CompareInfo - "+culture);
812                 }
813
814                 /* LAMESPEC: not mentioned in the spec, but corcompare
815                  * shows it.  Some documentation about what it does
816                  * would be nice.
817                  */
818                 public int LCID {
819                         get {
820                                 return culture;
821                         }
822                 }
823
824                 [ComVisible (false)]
825                 public virtual string Name {
826                         get { return m_name; }
827                 }
828         }
829 }