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
44 [StructLayout (LayoutKind.Sequential)]
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 void IDeserializationCallback.OnDeserialization(object sender)
59 if (UseManagedCollation) {
60 collator = new SimpleCollator (new CultureInfo (culture));
62 /* This will build the ICU collator, and store
63 * the pointer in ICU_collator
67 this.construct_compareinfo (icu_name);
69 // ICU_collator=IntPtr.Zero;
75 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
76 //private extern void construct_compareinfo (string locale);
78 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
79 //private extern void free_internal_collator ();
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);
87 [MethodImplAttribute (MethodImplOptions.InternalCall)]
88 private extern void assign_sortkey (object key, string source,
89 CompareOptions options);
91 [MethodImplAttribute (MethodImplOptions.InternalCall)]
92 private extern int internal_index (string source, int sindex,
93 int count, char value,
94 CompareOptions options,
97 [MethodImplAttribute (MethodImplOptions.InternalCall)]
98 private extern int internal_index (string source, int sindex,
99 int count, string value,
100 CompareOptions options,
104 public class CompareInfo {
105 internal static bool UseManagedCollation {
109 const CompareOptions ValidCompareOptions_NoStringSort =
110 CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace |
111 CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth |
112 CompareOptions.OrdinalIgnoreCase | CompareOptions.Ordinal;
114 const CompareOptions ValidCompareOptions = ValidCompareOptions_NoStringSort | CompareOptions.StringSort;
116 // Keep in synch with MonoCompareInfo in the runtime.
119 // private string icu_name;
121 // private IntPtr ICU_collator;
123 #pragma warning disable 169
124 private int win32LCID; // Unused, but MS.NET serializes this
125 #pragma warning restore 169
127 readonly string m_name; // MS.NET serializes this
130 SimpleCollator collator;
132 // Maps culture IDs to SimpleCollator objects
133 private static Hashtable collators;
136 // Protects access to 'collators'
137 private static object monitor = new Object ();
139 internal CompareInfo (CultureInfo ci)
141 this.culture = ci.LCID;
142 this.m_name = ci.Name;
143 if (UseManagedCollation) {
145 if (collators == null)
146 collators = new Hashtable ();
147 collator = (SimpleCollator)collators [ci.LCID];
148 if (collator == null) {
149 collator = new SimpleCollator (ci);
150 collators [ci.LCID] = collator;
156 this.icu_name = ci.IcuName;
157 this.construct_compareinfo (icu_name);
166 free_internal_collator ();
171 private int internal_compare_managed (string str1, int offset1,
172 int length1, string str2,
173 int offset2, int length2,
174 CompareOptions options)
176 return collator.Compare (str1, offset1, length1,
177 str2, offset2, length2, options);
180 private int internal_compare_switch (string str1, int offset1,
181 int length1, string str2,
182 int offset2, int length2,
183 CompareOptions options)
185 return UseManagedCollation ?
186 internal_compare_managed (str1, offset1, length1,
187 str2, offset2, length2, options) :
188 internal_compare (str1, offset1, length1,
189 str2, offset2, length2, options);
192 private int internal_compare_switch (string str1, int offset1,
193 int length1, string str2,
194 int offset2, int length2,
195 CompareOptions options)
197 return collator.Compare (str1, offset1, length1,
198 str2, offset2, length2, options);
201 public virtual int Compare (string string1, string string2)
203 return Compare (string1, string2, CompareOptions.None);
206 public virtual int Compare (string string1, string string2,
207 CompareOptions options)
209 if ((options & ValidCompareOptions) != options)
210 throw new ArgumentException ("options");
212 if (string1 == null) {
221 if(string1.Length == 0 && string2.Length == 0)
224 return(internal_compare_switch (string1, 0, string1.Length,
225 string2, 0, string2.Length,
229 public virtual int Compare (string string1, int offset1,
230 string string2, int offset2)
232 return Compare (string1, offset1, string2, offset2, CompareOptions.None);
235 public virtual int Compare (string string1, int offset1,
236 string string2, int offset2,
237 CompareOptions options)
239 if ((options & ValidCompareOptions) != options)
240 throw new ArgumentException ("options");
242 if (string1 == null) {
250 /* Not in the spec, but ms does these short
251 * cuts before checking the offsets (breaking
252 * the offset >= string length specified check
255 if((string1.Length == 0 || offset1 == string1.Length) &&
256 (string2.Length == 0 || offset2 == string2.Length))
259 if(offset1 < 0 || offset2 < 0) {
260 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
263 if(offset1 > string1.Length) {
264 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
267 if(offset2 > string2.Length) {
268 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
271 return(internal_compare_switch (string1, offset1,
272 string1.Length-offset1,
274 string2.Length-offset2,
278 public virtual int Compare (string string1, int offset1,
279 int length1, string string2,
280 int offset2, int length2)
282 return Compare (string1, offset1, length1, string2, offset2, length2, CompareOptions.None);
285 public virtual int Compare (string string1, int offset1,
286 int length1, string string2,
287 int offset2, int length2,
288 CompareOptions options)
290 if ((options & ValidCompareOptions) != options)
291 throw new ArgumentException ("options");
293 if (string1 == null) {
301 /* Not in the spec, but ms does these short
302 * cuts before checking the offsets (breaking
303 * the offset >= string length specified check
306 if((string1.Length == 0 ||
307 offset1 == string1.Length ||
309 (string2.Length == 0 ||
310 offset2 == string2.Length ||
314 if(offset1 < 0 || length1 < 0 ||
315 offset2 < 0 || length2 < 0) {
316 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
319 if(offset1 > string1.Length) {
320 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
323 if(offset2 > string2.Length) {
324 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
327 if(length1 > string1.Length-offset1) {
328 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
331 if(length2 > string2.Length-offset2) {
332 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
335 return(internal_compare_switch (string1, offset1, length1,
336 string2, offset2, length2,
340 public override bool Equals(object value)
342 CompareInfo other=value as CompareInfo;
347 return(other.culture==culture);
350 public static CompareInfo GetCompareInfo(int culture)
352 return(new CultureInfo (culture).CompareInfo);
355 public static CompareInfo GetCompareInfo(string name)
358 throw new ArgumentNullException("name");
360 return(new CultureInfo (name).CompareInfo);
363 public static CompareInfo GetCompareInfo(int culture,
366 /* The assembly parameter is supposedly there
367 * to allow some sort of compare algorithm
370 if(assembly == null) {
371 throw new ArgumentNullException("assembly");
373 if(assembly!=typeof (Object).Module.Assembly) {
374 throw new ArgumentException ("Assembly is an invalid type");
376 return(GetCompareInfo (culture));
379 public static CompareInfo GetCompareInfo(string name,
382 /* The assembly parameter is supposedly there
383 * to allow some sort of compare algorithm
387 throw new ArgumentNullException("name");
389 if(assembly == null) {
390 throw new ArgumentNullException("assembly");
392 if(assembly!=typeof (Object).Module.Assembly) {
393 throw new ArgumentException ("Assembly is an invalid type");
395 return(GetCompareInfo (name));
398 public override int GetHashCode()
403 public virtual SortKey GetSortKey(string source)
405 return(GetSortKey (source, CompareOptions.None));
408 public virtual SortKey GetSortKey(string source,
409 CompareOptions options)
412 case CompareOptions.Ordinal:
413 case CompareOptions.OrdinalIgnoreCase:
414 throw new ArgumentException ("Now allowed CompareOptions.", "options");
417 if (UseManagedCollation)
418 return collator.GetSortKey (source, options);
419 SortKey key=new SortKey (culture, source, options);
421 /* Need to do the icall here instead of in the
422 * SortKey constructor, as we need access to
423 * this instance's collator.
425 assign_sortkey (key, source, options);
429 return collator.GetSortKey (source, options);
433 public virtual int IndexOf (string source, char value)
435 return(IndexOf (source, value, 0, source.Length,
436 CompareOptions.None));
439 public virtual int IndexOf (string source, string value)
441 return(IndexOf (source, value, 0, source.Length,
442 CompareOptions.None));
445 public virtual int IndexOf (string source, char value,
446 CompareOptions options)
448 return(IndexOf (source, value, 0, source.Length,
452 public virtual int IndexOf (string source, char value,
455 return(IndexOf (source, value, startIndex,
456 source.Length - startIndex,
457 CompareOptions.None));
460 public virtual int IndexOf (string source, string value,
461 CompareOptions options)
463 return(IndexOf (source, value, 0, source.Length,
467 public virtual int IndexOf (string source, string value,
470 return(IndexOf (source, value, startIndex,
471 source.Length - startIndex,
472 CompareOptions.None));
475 public virtual int IndexOf (string source, char value,
477 CompareOptions options)
479 return(IndexOf (source, value, startIndex,
480 source.Length - startIndex, options));
483 public virtual int IndexOf (string source, char value,
484 int startIndex, int count)
486 return IndexOf (source, value, startIndex, count,
487 CompareOptions.None);
490 public virtual int IndexOf (string source, string value,
492 CompareOptions options)
494 return(IndexOf (source, value, startIndex,
495 source.Length - startIndex, options));
498 public virtual int IndexOf (string source, string value,
499 int startIndex, int count)
501 return(IndexOf (source, value, startIndex, count,
502 CompareOptions.None));
506 private int internal_index_managed (string s, int sindex,
507 int count, char c, CompareOptions opt,
511 collator.IndexOf (s, c, sindex, count, opt) :
512 collator.LastIndexOf (s, c, sindex, count, opt);
515 private int internal_index_switch (string s, int sindex,
516 int count, char c, CompareOptions opt,
519 // - forward IndexOf() icall is much faster than
520 // manged version, so always use icall. However,
521 // it does not work for OrdinalIgnoreCase, so
522 // do not avoid managed collator for that option.
523 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
524 internal_index_managed (s, sindex, count, c, opt, first) :
525 internal_index (s, sindex, count, c, opt, first);
528 private int internal_index_switch (string s, int sindex,
529 int count, char c, CompareOptions opt,
533 collator.IndexOf (s, c, sindex, count, opt) :
534 collator.LastIndexOf (s, c, sindex, count, opt);
538 public virtual int IndexOf (string source, char value,
539 int startIndex, int count,
540 CompareOptions options)
543 throw new ArgumentNullException ("source");
546 throw new ArgumentOutOfRangeException ("startIndex");
548 if(count<0 || (source.Length - startIndex) < count) {
549 throw new ArgumentOutOfRangeException ("count");
551 if ((options & ValidCompareOptions_NoStringSort) != options)
552 throw new ArgumentException ("options");
558 if((options & CompareOptions.Ordinal)!=0) {
559 for(int pos=startIndex;
560 pos < startIndex + count;
562 if(source[pos]==value) {
568 return (internal_index_switch (source, startIndex,
569 count, value, options,
575 private int internal_index_managed (string s1, int sindex,
576 int count, string s2, CompareOptions opt,
580 collator.IndexOf (s1, s2, sindex, count, opt) :
581 collator.LastIndexOf (s1, s2, sindex, count, opt);
584 private int internal_index_switch (string s1, int sindex,
585 int count, string s2, CompareOptions opt,
588 // - forward IndexOf() icall is much faster than
589 // manged version, so always use icall. However,
590 // it does not work for OrdinalIgnoreCase, so
591 // do not avoid managed collator for that option.
592 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
593 internal_index_managed (s1, sindex, count, s2, opt, first) :
594 internal_index (s1, sindex, count, s2, opt, first);
597 private int internal_index_switch (string s1, int sindex,
598 int count, string s2, CompareOptions opt,
602 collator.IndexOf (s1, s2, sindex, count, opt) :
603 collator.LastIndexOf (s1, s2, sindex, count, opt);
607 public virtual int IndexOf (string source, string value,
608 int startIndex, int count,
609 CompareOptions options)
612 throw new ArgumentNullException ("source");
615 throw new ArgumentNullException ("value");
618 throw new ArgumentOutOfRangeException ("startIndex");
620 if(count<0 || (source.Length - startIndex) < count) {
621 throw new ArgumentOutOfRangeException ("count");
623 if ((options & ValidCompareOptions_NoStringSort) != options)
624 throw new ArgumentException ("options");
625 if(value.Length==0) {
632 return (internal_index_switch (source, startIndex, count,
633 value, options, true));
636 public virtual bool IsPrefix(string source, string prefix)
638 return(IsPrefix (source, prefix, CompareOptions.None));
641 public virtual bool IsPrefix(string source, string prefix,
642 CompareOptions options)
645 throw new ArgumentNullException("source");
648 throw new ArgumentNullException("prefix");
651 if ((options & ValidCompareOptions_NoStringSort) != options)
652 throw new ArgumentException ("options");
654 if (UseManagedCollation)
655 return collator.IsPrefix (source, prefix, options);
657 if(source.Length < prefix.Length) {
660 return(Compare (source, 0, prefix.Length,
661 prefix, 0, prefix.Length,
666 public virtual bool IsSuffix(string source, string suffix)
668 return(IsSuffix (source, suffix, CompareOptions.None));
671 public virtual bool IsSuffix(string source, string suffix,
672 CompareOptions options)
675 throw new ArgumentNullException("source");
678 throw new ArgumentNullException("suffix");
681 if ((options & ValidCompareOptions_NoStringSort) != options)
682 throw new ArgumentException ("options");
684 if (UseManagedCollation)
685 return collator.IsSuffix (source, suffix, options);
687 if(source.Length < suffix.Length) {
690 return(Compare (source,
691 source.Length - suffix.Length,
692 suffix.Length, suffix, 0,
693 suffix.Length, options)==0);
697 public virtual int LastIndexOf(string source, char value)
699 return(LastIndexOf (source, value, source.Length - 1,
700 source.Length, CompareOptions.None));
703 public virtual int LastIndexOf(string source, string value)
705 return(LastIndexOf (source, value, source.Length - 1,
706 source.Length, CompareOptions.None));
709 public virtual int LastIndexOf(string source, char value,
710 CompareOptions options)
712 return(LastIndexOf (source, value, source.Length - 1,
713 source.Length, options));
716 public virtual int LastIndexOf(string source, char value,
719 return(LastIndexOf (source, value, startIndex,
721 CompareOptions.None));
724 public virtual int LastIndexOf(string source, string value,
725 CompareOptions options)
727 return(LastIndexOf (source, value, source.Length - 1,
728 source.Length, options));
731 public virtual int LastIndexOf(string source, string value,
734 return(LastIndexOf (source, value, startIndex,
736 CompareOptions.None));
739 public virtual int LastIndexOf(string source, char value,
741 CompareOptions options)
743 return(LastIndexOf (source, value, startIndex,
748 public virtual int LastIndexOf(string source, char value,
749 int startIndex, int count)
751 return(LastIndexOf (source, value, startIndex, count,
752 CompareOptions.None));
755 public virtual int LastIndexOf(string source, string value,
757 CompareOptions options)
759 return(LastIndexOf (source, value, startIndex,
764 public virtual int LastIndexOf(string source, string value,
765 int startIndex, int count)
767 return(LastIndexOf (source, value, startIndex, count,
768 CompareOptions.None));
771 public virtual int LastIndexOf(string source, char value,
772 int startIndex, int count,
773 CompareOptions options)
776 throw new ArgumentNullException("source");
779 throw new ArgumentOutOfRangeException ("startIndex");
781 if(count < 0 || (startIndex - count) < -1) {
782 throw new ArgumentOutOfRangeException("count");
784 if ((options & ValidCompareOptions_NoStringSort) != options)
785 throw new ArgumentException ("options");
791 if((options & CompareOptions.Ordinal)!=0) {
792 for(int pos=startIndex;
793 pos > startIndex - count;
795 if(source[pos]==value) {
801 return (internal_index_switch (source, startIndex,
802 count, value, options,
807 public virtual int LastIndexOf(string source, string value,
808 int startIndex, int count,
809 CompareOptions options)
812 throw new ArgumentNullException("source");
815 throw new ArgumentNullException("value");
818 throw new ArgumentOutOfRangeException ("startIndex");
820 if(count < 0 || (startIndex - count) < -1) {
821 throw new ArgumentOutOfRangeException("count");
823 if ((options & ValidCompareOptions_NoStringSort) != options)
824 throw new ArgumentException ("options");
829 int valuelen=value.Length;
834 return(internal_index_switch (source, startIndex, count,
835 value, options, false));
839 public static bool IsSortable (char ch)
841 return MSCompatUnicodeTable.IsSortable (ch);
845 public static bool IsSortable (string text)
847 return MSCompatUnicodeTable.IsSortable (text);
850 public override string ToString()
852 return("CompareInfo - "+culture);
855 /* LAMESPEC: not mentioned in the spec, but corcompare
856 * shows it. Some documentation about what it does
866 public virtual string Name {
867 get { return m_name; }