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.Generic;
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,
103 const CompareOptions ValidCompareOptions_NoStringSort =
104 CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace |
105 CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth |
106 CompareOptions.OrdinalIgnoreCase | CompareOptions.Ordinal;
108 const CompareOptions ValidCompareOptions = ValidCompareOptions_NoStringSort | CompareOptions.StringSort;
110 // Keep in synch with MonoCompareInfo in the runtime.
113 // private string icu_name;
115 // private IntPtr ICU_collator;
117 #pragma warning disable 169
118 private int win32LCID; // Unused, but MS.NET serializes this
119 #pragma warning restore 169
121 readonly string m_name; // MS.NET serializes this
124 SimpleCollator collator;
126 // Maps culture IDs to SimpleCollator objects
127 private static Dictionary<int, SimpleCollator> collators;
130 // Protects access to 'collators'
131 private static object monitor = new Object ();
133 internal CompareInfo (CultureInfo ci)
135 this.culture = ci.LCID;
136 this.m_name = ci.Name;
137 if (UseManagedCollation) {
139 if (collators == null)
140 collators = new Dictionary<int, SimpleCollator> ();
142 if (!collators.TryGetValue (ci.LCID, out collator)) {
143 collator = new SimpleCollator (ci);
144 collators [ci.LCID] = collator;
149 this.icu_name = ci.IcuName;
150 this.construct_compareinfo (icu_name);
157 free_internal_collator ();
160 private int internal_compare_managed (string str1, int offset1,
161 int length1, string str2,
162 int offset2, int length2,
163 CompareOptions options)
165 return collator.Compare (str1, offset1, length1,
166 str2, offset2, length2, options);
169 private int internal_compare_switch (string str1, int offset1,
170 int length1, string str2,
171 int offset2, int length2,
172 CompareOptions options)
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);
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);
186 public virtual int Compare (string string1, string string2)
188 return Compare (string1, string2, CompareOptions.None);
191 public virtual int Compare (string string1, string string2,
192 CompareOptions options)
194 if ((options & ValidCompareOptions) != options)
195 throw new ArgumentException ("options");
197 if (string1 == null) {
206 if(string1.Length == 0 && string2.Length == 0)
209 return(internal_compare_switch (string1, 0, string1.Length,
210 string2, 0, string2.Length,
214 public virtual int Compare (string string1, int offset1,
215 string string2, int offset2)
217 return Compare (string1, offset1, string2, offset2, CompareOptions.None);
220 public virtual int Compare (string string1, int offset1,
221 string string2, int offset2,
222 CompareOptions options)
224 if ((options & ValidCompareOptions) != options)
225 throw new ArgumentException ("options");
227 if (string1 == null) {
235 /* Not in the spec, but ms does these short
236 * cuts before checking the offsets (breaking
237 * the offset >= string length specified check
240 if((string1.Length == 0 || offset1 == string1.Length) &&
241 (string2.Length == 0 || offset2 == string2.Length))
244 if(offset1 < 0 || offset2 < 0) {
245 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
248 if(offset1 > string1.Length) {
249 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
252 if(offset2 > string2.Length) {
253 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
256 return(internal_compare_switch (string1, offset1,
257 string1.Length-offset1,
259 string2.Length-offset2,
263 public virtual int Compare (string string1, int offset1,
264 int length1, string string2,
265 int offset2, int length2)
267 return Compare (string1, offset1, length1, string2, offset2, length2, CompareOptions.None);
270 public virtual int Compare (string string1, int offset1,
271 int length1, string string2,
272 int offset2, int length2,
273 CompareOptions options)
275 if ((options & ValidCompareOptions) != options)
276 throw new ArgumentException ("options");
278 if (string1 == null) {
286 /* Not in the spec, but ms does these short
287 * cuts before checking the offsets (breaking
288 * the offset >= string length specified check
291 if((string1.Length == 0 ||
292 offset1 == string1.Length ||
294 (string2.Length == 0 ||
295 offset2 == string2.Length ||
299 if(offset1 < 0 || length1 < 0 ||
300 offset2 < 0 || length2 < 0) {
301 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
304 if(offset1 > string1.Length) {
305 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
308 if(offset2 > string2.Length) {
309 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
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");
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");
320 return(internal_compare_switch (string1, offset1, length1,
321 string2, offset2, length2,
325 public override bool Equals(object value)
327 CompareInfo other=value as CompareInfo;
332 return(other.culture==culture);
335 public static CompareInfo GetCompareInfo(int culture)
337 return(new CultureInfo (culture).CompareInfo);
340 public static CompareInfo GetCompareInfo(string name)
343 throw new ArgumentNullException("name");
345 return(new CultureInfo (name).CompareInfo);
348 public static CompareInfo GetCompareInfo(int culture,
351 /* The assembly parameter is supposedly there
352 * to allow some sort of compare algorithm
355 if(assembly == null) {
356 throw new ArgumentNullException("assembly");
358 if(assembly!=typeof (Object).Module.Assembly) {
359 throw new ArgumentException ("Assembly is an invalid type");
361 return(GetCompareInfo (culture));
364 public static CompareInfo GetCompareInfo(string name,
367 /* The assembly parameter is supposedly there
368 * to allow some sort of compare algorithm
372 throw new ArgumentNullException("name");
374 if(assembly == null) {
375 throw new ArgumentNullException("assembly");
377 if(assembly!=typeof (Object).Module.Assembly) {
378 throw new ArgumentException ("Assembly is an invalid type");
380 return(GetCompareInfo (name));
383 public override int GetHashCode()
388 internal int GetHashCodeOfString (string source, CompareOptions options)
390 return GetSortKey (source, options).GetHashCode ();
393 public virtual SortKey GetSortKey(string source)
395 return(GetSortKey (source, CompareOptions.None));
398 public virtual SortKey GetSortKey(string source,
399 CompareOptions options)
402 case CompareOptions.Ordinal:
403 case CompareOptions.OrdinalIgnoreCase:
404 throw new ArgumentException ("Now allowed CompareOptions.", "options");
406 if (UseManagedCollation)
407 return collator.GetSortKey (source, options);
408 SortKey key=new SortKey (culture, source, options);
410 /* Need to do the icall here instead of in the
411 * SortKey constructor, as we need access to
412 * this instance's collator.
414 assign_sortkey (key, source, options);
419 public virtual int IndexOf (string source, char value)
421 return(IndexOf (source, value, 0, source.Length,
422 CompareOptions.None));
425 public virtual int IndexOf (string source, string value)
427 return(IndexOf (source, value, 0, source.Length,
428 CompareOptions.None));
431 public virtual int IndexOf (string source, char value,
432 CompareOptions options)
434 return(IndexOf (source, value, 0, source.Length,
438 public virtual int IndexOf (string source, char value,
441 return(IndexOf (source, value, startIndex,
442 source.Length - startIndex,
443 CompareOptions.None));
446 public virtual int IndexOf (string source, string value,
447 CompareOptions options)
449 return(IndexOf (source, value, 0, source.Length,
453 public virtual int IndexOf (string source, string value,
456 return(IndexOf (source, value, startIndex,
457 source.Length - startIndex,
458 CompareOptions.None));
461 public virtual int IndexOf (string source, char value,
463 CompareOptions options)
465 return(IndexOf (source, value, startIndex,
466 source.Length - startIndex, options));
469 public virtual int IndexOf (string source, char value,
470 int startIndex, int count)
472 return IndexOf (source, value, startIndex, count,
473 CompareOptions.None);
476 public virtual int IndexOf (string source, string value,
478 CompareOptions options)
480 return(IndexOf (source, value, startIndex,
481 source.Length - startIndex, options));
484 public virtual int IndexOf (string source, string value,
485 int startIndex, int count)
487 return(IndexOf (source, value, startIndex, count,
488 CompareOptions.None));
491 private int internal_index_managed (string s, int sindex,
492 int count, char c, CompareOptions opt,
496 collator.IndexOf (s, c, sindex, count, opt) :
497 collator.LastIndexOf (s, c, sindex, count, opt);
500 private int internal_index_switch (string s, int sindex,
501 int count, char c, CompareOptions opt,
504 // - forward IndexOf() icall is much faster than
505 // manged version, so always use icall. However,
506 // it does not work for OrdinalIgnoreCase, so
507 // do not avoid managed collator for that option.
508 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
509 internal_index_managed (s, sindex, count, c, opt, first) :
510 internal_index (s, sindex, count, c, opt, first);
513 public virtual int IndexOf (string source, char value,
514 int startIndex, int count,
515 CompareOptions options)
518 throw new ArgumentNullException ("source");
521 throw new ArgumentOutOfRangeException ("startIndex");
523 if(count<0 || (source.Length - startIndex) < count) {
524 throw new ArgumentOutOfRangeException ("count");
526 if ((options & ValidCompareOptions_NoStringSort) != options)
527 throw new ArgumentException ("options");
533 if((options & CompareOptions.Ordinal)!=0) {
534 for(int pos=startIndex;
535 pos < startIndex + count;
537 if(source[pos]==value) {
543 return (internal_index_switch (source, startIndex,
544 count, value, options,
549 private int internal_index_managed (string s1, int sindex,
550 int count, string s2, CompareOptions opt,
554 collator.IndexOf (s1, s2, sindex, count, opt) :
555 collator.LastIndexOf (s1, s2, sindex, count, opt);
558 private int internal_index_switch (string s1, int sindex,
559 int count, string s2, CompareOptions opt,
562 // - forward IndexOf() icall is much faster than
563 // manged version, so always use icall. However,
564 // it does not work for OrdinalIgnoreCase, so
565 // do not avoid managed collator for that option.
566 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
567 internal_index_managed (s1, sindex, count, s2, opt, first) :
568 internal_index (s1, sindex, count, s2, opt, first);
571 public virtual int IndexOf (string source, string value,
572 int startIndex, int count,
573 CompareOptions options)
576 throw new ArgumentNullException ("source");
579 throw new ArgumentNullException ("value");
582 throw new ArgumentOutOfRangeException ("startIndex");
584 if(count<0 || (source.Length - startIndex) < count) {
585 throw new ArgumentOutOfRangeException ("count");
587 if ((options & ValidCompareOptions_NoStringSort) != options)
588 throw new ArgumentException ("options");
589 if(value.Length==0) {
596 return (internal_index_switch (source, startIndex, count,
597 value, options, true));
600 public virtual bool IsPrefix(string source, string prefix)
602 return(IsPrefix (source, prefix, CompareOptions.None));
605 public virtual bool IsPrefix(string source, string prefix,
606 CompareOptions options)
609 throw new ArgumentNullException("source");
612 throw new ArgumentNullException("prefix");
615 if ((options & ValidCompareOptions_NoStringSort) != options)
616 throw new ArgumentException ("options");
618 if (UseManagedCollation)
619 return collator.IsPrefix (source, prefix, options);
621 if(source.Length < prefix.Length) {
624 return(Compare (source, 0, prefix.Length,
625 prefix, 0, prefix.Length,
630 public virtual bool IsSuffix(string source, string suffix)
632 return(IsSuffix (source, suffix, CompareOptions.None));
635 public virtual bool IsSuffix(string source, string suffix,
636 CompareOptions options)
639 throw new ArgumentNullException("source");
642 throw new ArgumentNullException("suffix");
645 if ((options & ValidCompareOptions_NoStringSort) != options)
646 throw new ArgumentException ("options");
648 if (UseManagedCollation)
649 return collator.IsSuffix (source, suffix, options);
651 if(source.Length < suffix.Length) {
654 return(Compare (source,
655 source.Length - suffix.Length,
656 suffix.Length, suffix, 0,
657 suffix.Length, options)==0);
661 public virtual int LastIndexOf(string source, char value)
663 return(LastIndexOf (source, value, source.Length - 1,
664 source.Length, CompareOptions.None));
667 public virtual int LastIndexOf(string source, string value)
669 return(LastIndexOf (source, value, source.Length - 1,
670 source.Length, CompareOptions.None));
673 public virtual int LastIndexOf(string source, char value,
674 CompareOptions options)
676 return(LastIndexOf (source, value, source.Length - 1,
677 source.Length, options));
680 public virtual int LastIndexOf(string source, char value,
683 return(LastIndexOf (source, value, startIndex,
685 CompareOptions.None));
688 public virtual int LastIndexOf(string source, string value,
689 CompareOptions options)
691 return(LastIndexOf (source, value, source.Length - 1,
692 source.Length, options));
695 public virtual int LastIndexOf(string source, string value,
698 return(LastIndexOf (source, value, startIndex,
700 CompareOptions.None));
703 public virtual int LastIndexOf(string source, char value,
705 CompareOptions options)
707 return(LastIndexOf (source, value, startIndex,
712 public virtual int LastIndexOf(string source, char value,
713 int startIndex, int count)
715 return(LastIndexOf (source, value, startIndex, count,
716 CompareOptions.None));
719 public virtual int LastIndexOf(string source, string value,
721 CompareOptions options)
723 return(LastIndexOf (source, value, startIndex,
728 public virtual int LastIndexOf(string source, string value,
729 int startIndex, int count)
731 return(LastIndexOf (source, value, startIndex, count,
732 CompareOptions.None));
735 public virtual int LastIndexOf(string source, char value,
736 int startIndex, int count,
737 CompareOptions options)
740 throw new ArgumentNullException("source");
743 throw new ArgumentOutOfRangeException ("startIndex");
745 if(count < 0 || (startIndex - count) < -1) {
746 throw new ArgumentOutOfRangeException("count");
748 if ((options & ValidCompareOptions_NoStringSort) != options)
749 throw new ArgumentException ("options");
755 if((options & CompareOptions.Ordinal)!=0) {
756 for(int pos=startIndex;
757 pos > startIndex - count;
759 if(source[pos]==value) {
765 return (internal_index_switch (source, startIndex,
766 count, value, options,
771 public virtual int LastIndexOf(string source, string value,
772 int startIndex, int count,
773 CompareOptions options)
776 throw new ArgumentNullException("source");
779 throw new ArgumentNullException("value");
782 throw new ArgumentOutOfRangeException ("startIndex");
784 if(count < 0 || (startIndex - count) < -1) {
785 throw new ArgumentOutOfRangeException("count");
787 if ((options & ValidCompareOptions_NoStringSort) != options)
788 throw new ArgumentException ("options");
793 int valuelen=value.Length;
798 return(internal_index_switch (source, startIndex, count,
799 value, options, false));
803 public static bool IsSortable (char ch)
805 return MSCompatUnicodeTable.IsSortable (ch);
809 public static bool IsSortable (string text)
811 return MSCompatUnicodeTable.IsSortable (text);
814 public override string ToString()
816 return("CompareInfo - "+culture);
819 /* LAMESPEC: not mentioned in the spec, but corcompare
820 * shows it. Some documentation about what it does
830 public virtual string Name {
831 get { return m_name; }