2 // System.Globalization.CompareInfo
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Dick Porter (dick@ximian.com)
8 // (C) Ximian, Inc. 2002
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
34 using System.Reflection;
35 using System.Runtime.Serialization;
36 using System.Runtime.CompilerServices;
37 using Mono.Globalization.Unicode;
39 namespace System.Globalization
42 public class CompareInfo : IDeserializationCallback
44 static readonly bool useManagedCollation =
45 Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
46 != "yes" && MSCompatUnicodeTable.IsReady;
48 internal static bool UseManagedCollation {
49 get { return useManagedCollation; }
52 // Keep in synch with MonoCompareInfo in the runtime.
55 private string icu_name;
57 private IntPtr ICU_collator;
58 private int win32LCID; // Unused, but MS.NET serializes this
60 private string m_name; // Unused, but MS.NET serializes this
64 SimpleCollator collator;
66 /* Hide the .ctor() */
69 [MethodImplAttribute (MethodImplOptions.InternalCall)]
70 private extern void construct_compareinfo (string locale);
72 internal CompareInfo (CultureInfo ci)
74 this.culture = ci.LCID;
75 if (UseManagedCollation)
76 collator = new SimpleCollator (ci);
78 this.icu_name = ci.IcuName;
79 this.construct_compareinfo (icu_name);
83 [MethodImplAttribute (MethodImplOptions.InternalCall)]
84 private extern void free_internal_collator ();
88 free_internal_collator ();
91 [MethodImplAttribute (MethodImplOptions.InternalCall)]
92 private extern int internal_compare (string str1, int offset1,
93 int length1, string str2,
94 int offset2, int length2,
95 CompareOptions options);
97 private int internal_compare_managed (string str1, int offset1,
98 int length1, string str2,
99 int offset2, int length2,
100 CompareOptions options)
102 return collator.Compare (str1, offset1, length1,
103 str2, offset2, length2, options);
106 private int internal_compare_switch (string str1, int offset1,
107 int length1, string str2,
108 int offset2, int length2,
109 CompareOptions options)
111 return UseManagedCollation ?
112 internal_compare_managed (str1, offset1, length1,
113 str2, offset2, length2, options) :
114 internal_compare (str1, offset1, length1,
115 str2, offset2, length2, options);
118 public virtual int Compare (string string1, string string2)
120 if (string1 == null) {
129 if(string1.Length == 0 && string2.Length == 0)
132 return(internal_compare_switch (string1, 0, string1.Length,
133 string2, 0, string2.Length,
134 CompareOptions.None));
137 public virtual int Compare (string string1, string string2,
138 CompareOptions options)
140 if (string1 == null) {
149 if(string1.Length == 0 && string2.Length == 0)
152 return(internal_compare_switch (string1, 0, string1.Length,
153 string2, 0, string2.Length,
157 public virtual int Compare (string string1, int offset1,
158 string string2, int offset2)
160 if (string1 == null) {
168 /* Not in the spec, but ms does these short
169 * cuts before checking the offsets (breaking
170 * the offset >= string length specified check
173 if ((string1.Length == 0 || offset1 == string1.Length) &&
174 (string2.Length == 0 || offset2 == string2.Length))
177 if(offset1 < 0 || offset2 < 0) {
178 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
181 if(offset1 > string1.Length) {
182 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
185 if(offset2 > string2.Length) {
186 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
189 return(internal_compare_switch (string1, offset1,
190 string1.Length-offset1,
192 string2.Length-offset2,
193 CompareOptions.None));
196 public virtual int Compare (string string1, int offset1,
197 string string2, int offset2,
198 CompareOptions options)
200 if (string1 == null) {
208 /* Not in the spec, but ms does these short
209 * cuts before checking the offsets (breaking
210 * the offset >= string length specified check
213 if((string1.Length == 0 || offset1 == string1.Length) &&
214 (string2.Length == 0 || offset2 == string2.Length))
217 if(offset1 < 0 || offset2 < 0) {
218 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
221 if(offset1 > string1.Length) {
222 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
225 if(offset2 > string2.Length) {
226 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
229 return(internal_compare_switch (string1, offset1,
230 string1.Length-offset1,
232 string2.Length-offset1,
236 public virtual int Compare (string string1, int offset1,
237 int length1, string string2,
238 int offset2, int length2)
240 if (string1 == null) {
248 /* Not in the spec, but ms does these short
249 * cuts before checking the offsets (breaking
250 * the offset >= string length specified check
253 if((string1.Length == 0 ||
254 offset1 == string1.Length ||
256 (string2.Length == 0 ||
257 offset2 == string2.Length ||
261 if(offset1 < 0 || length1 < 0 ||
262 offset2 < 0 || length2 < 0) {
263 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
266 if(offset1 > string1.Length) {
267 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
270 if(offset2 > string2.Length) {
271 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
274 if(length1 > string1.Length-offset1) {
275 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
278 if(length2 > string2.Length-offset2) {
279 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
282 return(internal_compare_switch (string1, offset1, length1,
283 string2, offset2, length2,
284 CompareOptions.None));
287 public virtual int Compare (string string1, int offset1,
288 int length1, string string2,
289 int offset2, int length2,
290 CompareOptions options)
292 if (string1 == null) {
300 /* Not in the spec, but ms does these short
301 * cuts before checking the offsets (breaking
302 * the offset >= string length specified check
305 if((string1.Length == 0 ||
306 offset1 == string1.Length ||
308 (string2.Length == 0 ||
309 offset2 == string2.Length ||
313 if(offset1 < 0 || length1 < 0 ||
314 offset2 < 0 || length2 < 0) {
315 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
318 if(offset1 > string1.Length) {
319 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
322 if(offset2 > string2.Length) {
323 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
326 if(length1 > string1.Length-offset1) {
327 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
330 if(length2 > string2.Length-offset2) {
331 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
334 return(internal_compare_switch (string1, offset1, length1,
335 string2, offset2, length2,
339 public override bool Equals(object value)
341 CompareInfo other=value as CompareInfo;
346 return(other.culture==culture);
349 public static CompareInfo GetCompareInfo(int culture)
351 return(new CultureInfo (culture).CompareInfo);
354 public static CompareInfo GetCompareInfo(string name)
357 throw new ArgumentNullException("name");
359 return(new CultureInfo (name).CompareInfo);
362 public static CompareInfo GetCompareInfo(int culture,
365 /* The assembly parameter is supposedly there
366 * to allow some sort of compare algorithm
369 if(assembly == null) {
370 throw new ArgumentNullException("assembly");
372 if(assembly!=typeof (Object).Module.Assembly) {
373 throw new ArgumentException ("Assembly is an invalid type");
375 return(GetCompareInfo (culture));
378 public static CompareInfo GetCompareInfo(string name,
381 /* The assembly parameter is supposedly there
382 * to allow some sort of compare algorithm
386 throw new ArgumentNullException("name");
388 if(assembly == null) {
389 throw new ArgumentNullException("assembly");
391 if(assembly!=typeof (Object).Module.Assembly) {
392 throw new ArgumentException ("Assembly is an invalid type");
394 return(GetCompareInfo (name));
397 public override int GetHashCode()
402 [MethodImplAttribute (MethodImplOptions.InternalCall)]
403 private extern void assign_sortkey (object key, string source,
404 CompareOptions options);
406 public virtual SortKey GetSortKey(string source)
408 return(GetSortKey (source, CompareOptions.None));
411 public virtual SortKey GetSortKey(string source,
412 CompareOptions options)
416 case CompareOptions.Ordinal:
417 case CompareOptions.OrdinalIgnoreCase:
418 throw new ArgumentException ("Now allowed CompareOptions.", "options");
421 if (UseManagedCollation)
422 return collator.GetSortKey (source, options);
423 SortKey key=new SortKey (culture, source, options);
425 /* Need to do the icall here instead of in the
426 * SortKey constructor, as we need access to
427 * this instance's collator.
429 assign_sortkey (key, source, options);
434 public virtual int IndexOf (string source, char value)
436 return(IndexOf (source, value, 0, source.Length,
437 CompareOptions.None));
440 public virtual int IndexOf (string source, string value)
442 return(IndexOf (source, value, 0, source.Length,
443 CompareOptions.None));
446 public virtual int IndexOf (string source, char value,
447 CompareOptions options)
449 return(IndexOf (source, value, 0, source.Length,
453 public virtual int IndexOf (string source, char value,
456 return(IndexOf (source, value, startIndex,
457 source.Length - startIndex,
458 CompareOptions.None));
461 public virtual int IndexOf (string source, string value,
462 CompareOptions options)
464 return(IndexOf (source, value, 0, source.Length,
468 public virtual int IndexOf (string source, string value,
471 return(IndexOf (source, value, startIndex,
472 source.Length - startIndex,
473 CompareOptions.None));
476 public virtual int IndexOf (string source, char value,
478 CompareOptions options)
480 return(IndexOf (source, value, startIndex,
481 source.Length - startIndex, options));
484 public virtual int IndexOf (string source, char value,
485 int startIndex, int count)
487 return IndexOf (source, value, startIndex, count,
488 CompareOptions.None);
491 public virtual int IndexOf (string source, string value,
493 CompareOptions options)
495 return(IndexOf (source, value, startIndex,
496 source.Length - startIndex, options));
499 public virtual int IndexOf (string source, string value,
500 int startIndex, int count)
502 return(IndexOf (source, value, startIndex, count,
503 CompareOptions.None));
506 [MethodImplAttribute (MethodImplOptions.InternalCall)]
507 private extern int internal_index (string source, int sindex,
508 int count, char value,
509 CompareOptions options,
512 private int internal_index_managed (string s, int sindex,
513 int count, char c, CompareOptions opt,
517 collator.IndexOf (s, c, sindex, count, opt) :
518 collator.LastIndexOf (s, c, sindex, count, opt);
521 private int internal_index_switch (string s, int sindex,
522 int count, char c, CompareOptions opt,
525 // - forward IndexOf() icall is much faster than
526 // manged version, so always use icall. However,
527 // it does not work for OrdinalIgnoreCase, so
528 // do not avoid managed collator for that option.
529 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
530 internal_index_managed (s, sindex, count, c, opt, first) :
531 internal_index (s, sindex, count, c, opt, first);
534 public virtual int IndexOf (string source, char value,
535 int startIndex, int count,
536 CompareOptions options)
539 throw new ArgumentNullException ("source");
542 throw new ArgumentOutOfRangeException ("startIndex");
544 if(count<0 || (source.Length - startIndex) < count) {
545 throw new ArgumentOutOfRangeException ("count");
547 if((options & CompareOptions.StringSort)!=0) {
548 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
555 if((options & CompareOptions.Ordinal)!=0) {
556 for(int pos=startIndex;
557 pos < startIndex + count;
559 if(source[pos]==value) {
565 return (internal_index_switch (source, startIndex,
566 count, value, options,
571 [MethodImplAttribute (MethodImplOptions.InternalCall)]
572 private extern int internal_index (string source, int sindex,
573 int count, string value,
574 CompareOptions options,
577 private int internal_index_managed (string s1, int sindex,
578 int count, string s2, CompareOptions opt,
582 collator.IndexOf (s1, s2, sindex, count, opt) :
583 collator.LastIndexOf (s1, s2, sindex, count, opt);
586 private int internal_index_switch (string s1, int sindex,
587 int count, string s2, CompareOptions opt,
590 // - forward IndexOf() icall is much faster than
591 // manged version, so always use icall. However,
592 // it does not work for OrdinalIgnoreCase, so
593 // do not avoid managed collator for that option.
594 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
595 internal_index_managed (s1, sindex, count, s2, opt, first) :
596 internal_index (s1, sindex, count, s2, opt, first);
599 public virtual int IndexOf (string source, string value,
600 int startIndex, int count,
601 CompareOptions options)
604 throw new ArgumentNullException ("source");
607 throw new ArgumentNullException ("value");
610 throw new ArgumentOutOfRangeException ("startIndex");
612 if(count<0 || (source.Length - startIndex) < count) {
613 throw new ArgumentOutOfRangeException ("count");
619 return (internal_index_switch (source, startIndex, count,
620 value, options, true));
623 public virtual bool IsPrefix(string source, string prefix)
625 return(IsPrefix (source, prefix, CompareOptions.None));
628 public virtual bool IsPrefix(string source, string prefix,
629 CompareOptions options)
632 throw new ArgumentNullException("source");
635 throw new ArgumentNullException("prefix");
638 if (UseManagedCollation)
639 return collator.IsPrefix (source, prefix, options);
641 if(source.Length < prefix.Length) {
644 return(Compare (source, 0, prefix.Length,
645 prefix, 0, prefix.Length,
650 public virtual bool IsSuffix(string source, string suffix)
652 return(IsSuffix (source, suffix, CompareOptions.None));
655 public virtual bool IsSuffix(string source, string suffix,
656 CompareOptions options)
659 throw new ArgumentNullException("source");
662 throw new ArgumentNullException("suffix");
665 if (UseManagedCollation)
666 return collator.IsSuffix (source, suffix, options);
668 if(source.Length < suffix.Length) {
671 return(Compare (source,
672 source.Length - suffix.Length,
673 suffix.Length, suffix, 0,
674 suffix.Length, options)==0);
678 public virtual int LastIndexOf(string source, char value)
680 return(LastIndexOf (source, value, source.Length - 1,
681 source.Length, CompareOptions.None));
684 public virtual int LastIndexOf(string source, string value)
686 return(LastIndexOf (source, value, source.Length - 1,
687 source.Length, CompareOptions.None));
690 public virtual int LastIndexOf(string source, char value,
691 CompareOptions options)
693 return(LastIndexOf (source, value, source.Length - 1,
694 source.Length, options));
697 public virtual int LastIndexOf(string source, char value,
700 return(LastIndexOf (source, value, startIndex,
702 CompareOptions.None));
705 public virtual int LastIndexOf(string source, string value,
706 CompareOptions options)
708 return(LastIndexOf (source, value, source.Length - 1,
709 source.Length, options));
712 public virtual int LastIndexOf(string source, string value,
715 return(LastIndexOf (source, value, startIndex,
717 CompareOptions.None));
720 public virtual int LastIndexOf(string source, char value,
722 CompareOptions options)
724 return(LastIndexOf (source, value, startIndex,
729 public virtual int LastIndexOf(string source, char value,
730 int startIndex, int count)
732 return(LastIndexOf (source, value, startIndex, count,
733 CompareOptions.None));
736 public virtual int LastIndexOf(string source, string value,
738 CompareOptions options)
740 return(LastIndexOf (source, value, startIndex,
745 public virtual int LastIndexOf(string source, string value,
746 int startIndex, int count)
748 return(LastIndexOf (source, value, startIndex, count,
749 CompareOptions.None));
752 public virtual int LastIndexOf(string source, char value,
753 int startIndex, int count,
754 CompareOptions options)
757 throw new ArgumentNullException("source");
760 throw new ArgumentOutOfRangeException ("startIndex");
762 if(count < 0 || (startIndex - count) < -1) {
763 throw new ArgumentOutOfRangeException("count");
765 if((options & CompareOptions.StringSort)!=0) {
766 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
773 if((options & CompareOptions.Ordinal)!=0) {
774 for(int pos=startIndex;
775 pos > startIndex - count;
777 if(source[pos]==value) {
783 return (internal_index_switch (source, startIndex,
784 count, value, options,
789 public virtual int LastIndexOf(string source, string value,
790 int startIndex, int count,
791 CompareOptions options)
794 throw new ArgumentNullException("source");
797 throw new ArgumentNullException("value");
800 throw new ArgumentOutOfRangeException ("startIndex");
802 if(count < 0 || (startIndex - count) < -1) {
803 throw new ArgumentOutOfRangeException("count");
809 int valuelen=value.Length;
814 return(internal_index_switch (source, startIndex, count,
815 value, options, false));
819 public static bool IsSortable (char c)
821 return MSCompatUnicodeTable.IsSortable (c);
824 public static bool IsSortable (string s)
826 return MSCompatUnicodeTable.IsSortable (s);
830 public override string ToString()
832 return("CompareInfo - "+culture);
835 void IDeserializationCallback.OnDeserialization(object sender)
837 if (UseManagedCollation) {
838 collator = new SimpleCollator (new CultureInfo (culture));
840 /* This will build the ICU collator, and store
841 * the pointer in ICU_collator
844 this.construct_compareinfo (icu_name);
846 ICU_collator=IntPtr.Zero;
851 /* LAMESPEC: not mentioned in the spec, but corcompare
852 * shows it. Some documentation about what it does