2 // System.Globalization.CompareInfo.cs
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.Collections;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39 using Mono.Globalization.Unicode;
41 namespace System.Globalization
47 public class CompareInfo : IDeserializationCallback
49 static readonly bool useManagedCollation =
50 Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
51 != "yes" && MSCompatUnicodeTable.IsReady;
53 internal static bool UseManagedCollation {
54 get { return useManagedCollation; }
57 // Keep in synch with MonoCompareInfo in the runtime.
60 private string icu_name;
62 // private IntPtr ICU_collator;
64 #pragma warning disable 169
65 private int win32LCID; // Unused, but MS.NET serializes this
67 private string m_name; // Unused, but MS.NET serializes this
69 #pragma warning restore 169
72 SimpleCollator collator;
74 // Maps culture IDs to SimpleCollator objects
75 private static Hashtable collators;
78 // Protects access to 'collators'
79 private static object monitor = new Object ();
81 /* Hide the .ctor() */
84 [MethodImplAttribute (MethodImplOptions.InternalCall)]
85 private extern void construct_compareinfo (string locale);
87 internal CompareInfo (CultureInfo ci)
89 this.culture = ci.LCID;
90 if (UseManagedCollation) {
92 if (collators == null)
93 collators = new Hashtable ();
94 collator = (SimpleCollator)collators [ci.LCID];
95 if (collator == null) {
96 collator = new SimpleCollator (ci);
97 collators [ci.LCID] = collator;
101 this.icu_name = ci.IcuName;
102 this.construct_compareinfo (icu_name);
106 [MethodImplAttribute (MethodImplOptions.InternalCall)]
107 private extern void free_internal_collator ();
111 free_internal_collator ();
114 [MethodImplAttribute (MethodImplOptions.InternalCall)]
115 private extern int internal_compare (string str1, int offset1,
116 int length1, string str2,
117 int offset2, int length2,
118 CompareOptions options);
120 private int internal_compare_managed (string str1, int offset1,
121 int length1, string str2,
122 int offset2, int length2,
123 CompareOptions options)
125 return collator.Compare (str1, offset1, length1,
126 str2, offset2, length2, options);
129 private int internal_compare_switch (string str1, int offset1,
130 int length1, string str2,
131 int offset2, int length2,
132 CompareOptions options)
134 return UseManagedCollation ?
135 internal_compare_managed (str1, offset1, length1,
136 str2, offset2, length2, options) :
137 internal_compare (str1, offset1, length1,
138 str2, offset2, length2, options);
141 public virtual int Compare (string string1, string string2)
143 if (string1 == null) {
152 if(string1.Length == 0 && string2.Length == 0)
155 return(internal_compare_switch (string1, 0, string1.Length,
156 string2, 0, string2.Length,
157 CompareOptions.None));
160 public virtual int Compare (string string1, string string2,
161 CompareOptions options)
163 if (string1 == null) {
172 if(string1.Length == 0 && string2.Length == 0)
175 return(internal_compare_switch (string1, 0, string1.Length,
176 string2, 0, string2.Length,
180 public virtual int Compare (string string1, int offset1,
181 string string2, int offset2)
183 if (string1 == null) {
191 /* Not in the spec, but ms does these short
192 * cuts before checking the offsets (breaking
193 * the offset >= string length specified check
196 if ((string1.Length == 0 || offset1 == string1.Length) &&
197 (string2.Length == 0 || offset2 == string2.Length))
200 if(offset1 < 0 || offset2 < 0) {
201 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
204 if(offset1 > string1.Length) {
205 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
208 if(offset2 > string2.Length) {
209 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
212 return(internal_compare_switch (string1, offset1,
213 string1.Length-offset1,
215 string2.Length-offset2,
216 CompareOptions.None));
219 public virtual int Compare (string string1, int offset1,
220 string string2, int offset2,
221 CompareOptions options)
223 if (string1 == null) {
231 /* Not in the spec, but ms does these short
232 * cuts before checking the offsets (breaking
233 * the offset >= string length specified check
236 if((string1.Length == 0 || offset1 == string1.Length) &&
237 (string2.Length == 0 || offset2 == string2.Length))
240 if(offset1 < 0 || offset2 < 0) {
241 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
244 if(offset1 > string1.Length) {
245 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
248 if(offset2 > string2.Length) {
249 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
252 return(internal_compare_switch (string1, offset1,
253 string1.Length-offset1,
255 string2.Length-offset1,
259 public virtual int Compare (string string1, int offset1,
260 int length1, string string2,
261 int offset2, int length2)
263 if (string1 == null) {
271 /* Not in the spec, but ms does these short
272 * cuts before checking the offsets (breaking
273 * the offset >= string length specified check
276 if((string1.Length == 0 ||
277 offset1 == string1.Length ||
279 (string2.Length == 0 ||
280 offset2 == string2.Length ||
284 if(offset1 < 0 || length1 < 0 ||
285 offset2 < 0 || length2 < 0) {
286 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
289 if(offset1 > string1.Length) {
290 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
293 if(offset2 > string2.Length) {
294 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
297 if(length1 > string1.Length-offset1) {
298 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
301 if(length2 > string2.Length-offset2) {
302 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
305 return(internal_compare_switch (string1, offset1, length1,
306 string2, offset2, length2,
307 CompareOptions.None));
310 public virtual int Compare (string string1, int offset1,
311 int length1, string string2,
312 int offset2, int length2,
313 CompareOptions options)
315 if (string1 == null) {
323 /* Not in the spec, but ms does these short
324 * cuts before checking the offsets (breaking
325 * the offset >= string length specified check
328 if((string1.Length == 0 ||
329 offset1 == string1.Length ||
331 (string2.Length == 0 ||
332 offset2 == string2.Length ||
336 if(offset1 < 0 || length1 < 0 ||
337 offset2 < 0 || length2 < 0) {
338 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
341 if(offset1 > string1.Length) {
342 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
345 if(offset2 > string2.Length) {
346 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
349 if(length1 > string1.Length-offset1) {
350 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
353 if(length2 > string2.Length-offset2) {
354 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
357 return(internal_compare_switch (string1, offset1, length1,
358 string2, offset2, length2,
362 public override bool Equals(object value)
364 CompareInfo other=value as CompareInfo;
369 return(other.culture==culture);
372 public static CompareInfo GetCompareInfo(int culture)
374 return(new CultureInfo (culture).CompareInfo);
377 public static CompareInfo GetCompareInfo(string name)
380 throw new ArgumentNullException("name");
382 return(new CultureInfo (name).CompareInfo);
385 public static CompareInfo GetCompareInfo(int culture,
388 /* The assembly parameter is supposedly there
389 * to allow some sort of compare algorithm
392 if(assembly == null) {
393 throw new ArgumentNullException("assembly");
395 if(assembly!=typeof (Object).Module.Assembly) {
396 throw new ArgumentException ("Assembly is an invalid type");
398 return(GetCompareInfo (culture));
401 public static CompareInfo GetCompareInfo(string name,
404 /* The assembly parameter is supposedly there
405 * to allow some sort of compare algorithm
409 throw new ArgumentNullException("name");
411 if(assembly == null) {
412 throw new ArgumentNullException("assembly");
414 if(assembly!=typeof (Object).Module.Assembly) {
415 throw new ArgumentException ("Assembly is an invalid type");
417 return(GetCompareInfo (name));
420 public override int GetHashCode()
425 [MethodImplAttribute (MethodImplOptions.InternalCall)]
426 private extern void assign_sortkey (object key, string source,
427 CompareOptions options);
429 public virtual SortKey GetSortKey(string source)
431 return(GetSortKey (source, CompareOptions.None));
434 public virtual SortKey GetSortKey(string source,
435 CompareOptions options)
439 case CompareOptions.Ordinal:
440 case CompareOptions.OrdinalIgnoreCase:
441 throw new ArgumentException ("Now allowed CompareOptions.", "options");
444 if (UseManagedCollation)
445 return collator.GetSortKey (source, options);
446 SortKey key=new SortKey (culture, source, options);
448 /* Need to do the icall here instead of in the
449 * SortKey constructor, as we need access to
450 * this instance's collator.
452 assign_sortkey (key, source, options);
457 public virtual int IndexOf (string source, char value)
459 return(IndexOf (source, value, 0, source.Length,
460 CompareOptions.None));
463 public virtual int IndexOf (string source, string value)
465 return(IndexOf (source, value, 0, source.Length,
466 CompareOptions.None));
469 public virtual int IndexOf (string source, char value,
470 CompareOptions options)
472 return(IndexOf (source, value, 0, source.Length,
476 public virtual int IndexOf (string source, char value,
479 return(IndexOf (source, value, startIndex,
480 source.Length - startIndex,
481 CompareOptions.None));
484 public virtual int IndexOf (string source, string value,
485 CompareOptions options)
487 return(IndexOf (source, value, 0, source.Length,
491 public virtual int IndexOf (string source, string value,
494 return(IndexOf (source, value, startIndex,
495 source.Length - startIndex,
496 CompareOptions.None));
499 public virtual int IndexOf (string source, char value,
501 CompareOptions options)
503 return(IndexOf (source, value, startIndex,
504 source.Length - startIndex, options));
507 public virtual int IndexOf (string source, char value,
508 int startIndex, int count)
510 return IndexOf (source, value, startIndex, count,
511 CompareOptions.None);
514 public virtual int IndexOf (string source, string value,
516 CompareOptions options)
518 return(IndexOf (source, value, startIndex,
519 source.Length - startIndex, options));
522 public virtual int IndexOf (string source, string value,
523 int startIndex, int count)
525 return(IndexOf (source, value, startIndex, count,
526 CompareOptions.None));
529 [MethodImplAttribute (MethodImplOptions.InternalCall)]
530 private extern int internal_index (string source, int sindex,
531 int count, char value,
532 CompareOptions options,
535 private int internal_index_managed (string s, int sindex,
536 int count, char c, CompareOptions opt,
540 collator.IndexOf (s, c, sindex, count, opt) :
541 collator.LastIndexOf (s, c, sindex, count, opt);
544 private int internal_index_switch (string s, int sindex,
545 int count, char c, CompareOptions opt,
548 // - forward IndexOf() icall is much faster than
549 // manged version, so always use icall. However,
550 // it does not work for OrdinalIgnoreCase, so
551 // do not avoid managed collator for that option.
552 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
553 internal_index_managed (s, sindex, count, c, opt, first) :
554 internal_index (s, sindex, count, c, opt, first);
557 public virtual int IndexOf (string source, char value,
558 int startIndex, int count,
559 CompareOptions options)
562 throw new ArgumentNullException ("source");
565 throw new ArgumentOutOfRangeException ("startIndex");
567 if(count<0 || (source.Length - startIndex) < count) {
568 throw new ArgumentOutOfRangeException ("count");
570 if((options & CompareOptions.StringSort)!=0) {
571 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
578 if((options & CompareOptions.Ordinal)!=0) {
579 for(int pos=startIndex;
580 pos < startIndex + count;
582 if(source[pos]==value) {
588 return (internal_index_switch (source, startIndex,
589 count, value, options,
594 [MethodImplAttribute (MethodImplOptions.InternalCall)]
595 private extern int internal_index (string source, int sindex,
596 int count, string value,
597 CompareOptions options,
600 private int internal_index_managed (string s1, int sindex,
601 int count, string s2, CompareOptions opt,
605 collator.IndexOf (s1, s2, sindex, count, opt) :
606 collator.LastIndexOf (s1, s2, sindex, count, opt);
609 private int internal_index_switch (string s1, int sindex,
610 int count, string s2, CompareOptions opt,
613 // - forward IndexOf() icall is much faster than
614 // manged version, so always use icall. However,
615 // it does not work for OrdinalIgnoreCase, so
616 // do not avoid managed collator for that option.
617 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
618 internal_index_managed (s1, sindex, count, s2, opt, first) :
619 internal_index (s1, sindex, count, s2, opt, first);
622 public virtual int IndexOf (string source, string value,
623 int startIndex, int count,
624 CompareOptions options)
627 throw new ArgumentNullException ("source");
630 throw new ArgumentNullException ("value");
633 throw new ArgumentOutOfRangeException ("startIndex");
635 if(count<0 || (source.Length - startIndex) < count) {
636 throw new ArgumentOutOfRangeException ("count");
638 if(value.Length==0) {
645 return (internal_index_switch (source, startIndex, count,
646 value, options, true));
649 public virtual bool IsPrefix(string source, string prefix)
651 return(IsPrefix (source, prefix, CompareOptions.None));
654 public virtual bool IsPrefix(string source, string prefix,
655 CompareOptions options)
658 throw new ArgumentNullException("source");
661 throw new ArgumentNullException("prefix");
664 if (UseManagedCollation)
665 return collator.IsPrefix (source, prefix, options);
667 if(source.Length < prefix.Length) {
670 return(Compare (source, 0, prefix.Length,
671 prefix, 0, prefix.Length,
676 public virtual bool IsSuffix(string source, string suffix)
678 return(IsSuffix (source, suffix, CompareOptions.None));
681 public virtual bool IsSuffix(string source, string suffix,
682 CompareOptions options)
685 throw new ArgumentNullException("source");
688 throw new ArgumentNullException("suffix");
691 if (UseManagedCollation)
692 return collator.IsSuffix (source, suffix, options);
694 if(source.Length < suffix.Length) {
697 return(Compare (source,
698 source.Length - suffix.Length,
699 suffix.Length, suffix, 0,
700 suffix.Length, options)==0);
704 public virtual int LastIndexOf(string source, char value)
706 return(LastIndexOf (source, value, source.Length - 1,
707 source.Length, CompareOptions.None));
710 public virtual int LastIndexOf(string source, string value)
712 return(LastIndexOf (source, value, source.Length - 1,
713 source.Length, CompareOptions.None));
716 public virtual int LastIndexOf(string source, char value,
717 CompareOptions options)
719 return(LastIndexOf (source, value, source.Length - 1,
720 source.Length, options));
723 public virtual int LastIndexOf(string source, char value,
726 return(LastIndexOf (source, value, startIndex,
728 CompareOptions.None));
731 public virtual int LastIndexOf(string source, string value,
732 CompareOptions options)
734 return(LastIndexOf (source, value, source.Length - 1,
735 source.Length, options));
738 public virtual int LastIndexOf(string source, string value,
741 return(LastIndexOf (source, value, startIndex,
743 CompareOptions.None));
746 public virtual int LastIndexOf(string source, char value,
748 CompareOptions options)
750 return(LastIndexOf (source, value, startIndex,
755 public virtual int LastIndexOf(string source, char value,
756 int startIndex, int count)
758 return(LastIndexOf (source, value, startIndex, count,
759 CompareOptions.None));
762 public virtual int LastIndexOf(string source, string value,
764 CompareOptions options)
766 return(LastIndexOf (source, value, startIndex,
771 public virtual int LastIndexOf(string source, string value,
772 int startIndex, int count)
774 return(LastIndexOf (source, value, startIndex, count,
775 CompareOptions.None));
778 public virtual int LastIndexOf(string source, char value,
779 int startIndex, int count,
780 CompareOptions options)
783 throw new ArgumentNullException("source");
786 throw new ArgumentOutOfRangeException ("startIndex");
788 if(count < 0 || (startIndex - count) < -1) {
789 throw new ArgumentOutOfRangeException("count");
791 if((options & CompareOptions.StringSort)!=0) {
792 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
799 if((options & CompareOptions.Ordinal)!=0) {
800 for(int pos=startIndex;
801 pos > startIndex - count;
803 if(source[pos]==value) {
809 return (internal_index_switch (source, startIndex,
810 count, value, options,
815 public virtual int LastIndexOf(string source, string value,
816 int startIndex, int count,
817 CompareOptions options)
820 throw new ArgumentNullException("source");
823 throw new ArgumentNullException("value");
826 throw new ArgumentOutOfRangeException ("startIndex");
828 if(count < 0 || (startIndex - count) < -1) {
829 throw new ArgumentOutOfRangeException("count");
835 int valuelen=value.Length;
840 return(internal_index_switch (source, startIndex, count,
841 value, options, false));
846 public static bool IsSortable (char ch)
848 return MSCompatUnicodeTable.IsSortable (ch);
852 public static bool IsSortable (string text)
854 return MSCompatUnicodeTable.IsSortable (text);
858 public override string ToString()
860 return("CompareInfo - "+culture);
863 void IDeserializationCallback.OnDeserialization(object sender)
865 if (UseManagedCollation) {
866 collator = new SimpleCollator (new CultureInfo (culture));
868 /* This will build the ICU collator, and store
869 * the pointer in ICU_collator
872 this.construct_compareinfo (icu_name);
874 // ICU_collator=IntPtr.Zero;
879 /* LAMESPEC: not mentioned in the spec, but corcompare
880 * shows it. Some documentation about what it does
892 public virtual string Name {
893 get { return icu_name; }