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
66 this.construct_compareinfo (icu_name);
68 // ICU_collator=IntPtr.Zero;
73 [MethodImplAttribute (MethodImplOptions.InternalCall)]
74 private extern void construct_compareinfo (string locale);
76 [MethodImplAttribute (MethodImplOptions.InternalCall)]
77 private extern void free_internal_collator ();
79 [MethodImplAttribute (MethodImplOptions.InternalCall)]
80 private extern int internal_compare (string str1, int offset1,
81 int length1, string str2,
82 int offset2, int length2,
83 CompareOptions options);
85 [MethodImplAttribute (MethodImplOptions.InternalCall)]
86 private extern void assign_sortkey (object key, string source,
87 CompareOptions options);
89 [MethodImplAttribute (MethodImplOptions.InternalCall)]
90 private extern int internal_index (string source, int sindex,
91 int count, char value,
92 CompareOptions options,
95 [MethodImplAttribute (MethodImplOptions.InternalCall)]
96 private extern int internal_index (string source, int sindex,
97 int count, string value,
98 CompareOptions options,
102 public class CompareInfo {
103 internal static bool UseManagedCollation {
107 const CompareOptions ValidCompareOptions_NoStringSort =
108 CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace |
109 CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth |
110 CompareOptions.OrdinalIgnoreCase | CompareOptions.Ordinal;
112 const CompareOptions ValidCompareOptions = ValidCompareOptions_NoStringSort | CompareOptions.StringSort;
114 // Keep in synch with MonoCompareInfo in the runtime.
117 private string icu_name;
119 // private IntPtr ICU_collator;
121 #pragma warning disable 169
122 private int win32LCID; // Unused, but MS.NET serializes this
123 private string m_name; // Unused, but MS.NET serializes this
124 #pragma warning restore 169
127 SimpleCollator collator;
129 // Maps culture IDs to SimpleCollator objects
130 private static Hashtable collators;
133 // Protects access to 'collators'
134 private static object monitor = new Object ();
136 /* Hide the .ctor() */
139 internal CompareInfo (CultureInfo ci)
141 this.culture = ci.LCID;
142 if (UseManagedCollation) {
144 if (collators == null)
145 collators = new Hashtable ();
146 collator = (SimpleCollator)collators [ci.LCID];
147 if (collator == null) {
148 collator = new SimpleCollator (ci);
149 collators [ci.LCID] = collator;
154 this.icu_name = ci.IcuName;
155 this.construct_compareinfo (icu_name);
163 free_internal_collator ();
168 private int internal_compare_managed (string str1, int offset1,
169 int length1, string str2,
170 int offset2, int length2,
171 CompareOptions options)
173 return collator.Compare (str1, offset1, length1,
174 str2, offset2, length2, options);
177 private int internal_compare_switch (string str1, int offset1,
178 int length1, string str2,
179 int offset2, int length2,
180 CompareOptions options)
182 return UseManagedCollation ?
183 internal_compare_managed (str1, offset1, length1,
184 str2, offset2, length2, options) :
185 internal_compare (str1, offset1, length1,
186 str2, offset2, length2, options);
189 private int internal_compare_switch (string str1, int offset1,
190 int length1, string str2,
191 int offset2, int length2,
192 CompareOptions options)
194 return collator.Compare (str1, offset1, length1,
195 str2, offset2, length2, options);
198 public virtual int Compare (string string1, string string2)
200 return Compare (string1, string2, CompareOptions.None);
203 public virtual int Compare (string string1, string string2,
204 CompareOptions options)
206 if ((options & ValidCompareOptions) != options)
207 throw new ArgumentException ("options");
209 if (string1 == null) {
218 if(string1.Length == 0 && string2.Length == 0)
221 return(internal_compare_switch (string1, 0, string1.Length,
222 string2, 0, string2.Length,
226 public virtual int Compare (string string1, int offset1,
227 string string2, int offset2)
229 return Compare (string1, offset1, string2, offset2, CompareOptions.None);
232 public virtual int Compare (string string1, int offset1,
233 string string2, int offset2,
234 CompareOptions options)
236 if ((options & ValidCompareOptions) != options)
237 throw new ArgumentException ("options");
239 if (string1 == null) {
247 /* Not in the spec, but ms does these short
248 * cuts before checking the offsets (breaking
249 * the offset >= string length specified check
252 if((string1.Length == 0 || offset1 == string1.Length) &&
253 (string2.Length == 0 || offset2 == string2.Length))
256 if(offset1 < 0 || offset2 < 0) {
257 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
260 if(offset1 > string1.Length) {
261 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
264 if(offset2 > string2.Length) {
265 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
268 return(internal_compare_switch (string1, offset1,
269 string1.Length-offset1,
271 string2.Length-offset2,
275 public virtual int Compare (string string1, int offset1,
276 int length1, string string2,
277 int offset2, int length2)
279 return Compare (string1, offset1, length1, string2, offset2, length2, CompareOptions.None);
282 public virtual int Compare (string string1, int offset1,
283 int length1, string string2,
284 int offset2, int length2,
285 CompareOptions options)
287 if ((options & ValidCompareOptions) != options)
288 throw new ArgumentException ("options");
290 if (string1 == null) {
298 /* Not in the spec, but ms does these short
299 * cuts before checking the offsets (breaking
300 * the offset >= string length specified check
303 if((string1.Length == 0 ||
304 offset1 == string1.Length ||
306 (string2.Length == 0 ||
307 offset2 == string2.Length ||
311 if(offset1 < 0 || length1 < 0 ||
312 offset2 < 0 || length2 < 0) {
313 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
316 if(offset1 > string1.Length) {
317 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
320 if(offset2 > string2.Length) {
321 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
324 if(length1 > string1.Length-offset1) {
325 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
328 if(length2 > string2.Length-offset2) {
329 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
332 return(internal_compare_switch (string1, offset1, length1,
333 string2, offset2, length2,
337 public override bool Equals(object value)
339 CompareInfo other=value as CompareInfo;
344 return(other.culture==culture);
347 public static CompareInfo GetCompareInfo(int culture)
349 return(new CultureInfo (culture).CompareInfo);
352 public static CompareInfo GetCompareInfo(string name)
355 throw new ArgumentNullException("name");
357 return(new CultureInfo (name).CompareInfo);
360 public static CompareInfo GetCompareInfo(int culture,
363 /* The assembly parameter is supposedly there
364 * to allow some sort of compare algorithm
367 if(assembly == null) {
368 throw new ArgumentNullException("assembly");
370 if(assembly!=typeof (Object).Module.Assembly) {
371 throw new ArgumentException ("Assembly is an invalid type");
373 return(GetCompareInfo (culture));
376 public static CompareInfo GetCompareInfo(string name,
379 /* The assembly parameter is supposedly there
380 * to allow some sort of compare algorithm
384 throw new ArgumentNullException("name");
386 if(assembly == null) {
387 throw new ArgumentNullException("assembly");
389 if(assembly!=typeof (Object).Module.Assembly) {
390 throw new ArgumentException ("Assembly is an invalid type");
392 return(GetCompareInfo (name));
395 public override int GetHashCode()
400 public virtual SortKey GetSortKey(string source)
402 return(GetSortKey (source, CompareOptions.None));
405 public virtual SortKey GetSortKey(string source,
406 CompareOptions options)
409 case CompareOptions.Ordinal:
410 case CompareOptions.OrdinalIgnoreCase:
411 throw new ArgumentException ("Now allowed CompareOptions.", "options");
414 if (UseManagedCollation)
415 return collator.GetSortKey (source, options);
416 SortKey key=new SortKey (culture, source, options);
418 /* Need to do the icall here instead of in the
419 * SortKey constructor, as we need access to
420 * this instance's collator.
422 assign_sortkey (key, source, options);
426 return collator.GetSortKey (source, options);
430 public virtual int IndexOf (string source, char value)
432 return(IndexOf (source, value, 0, source.Length,
433 CompareOptions.None));
436 public virtual int IndexOf (string source, string value)
438 return(IndexOf (source, value, 0, source.Length,
439 CompareOptions.None));
442 public virtual int IndexOf (string source, char value,
443 CompareOptions options)
445 return(IndexOf (source, value, 0, source.Length,
449 public virtual int IndexOf (string source, char value,
452 return(IndexOf (source, value, startIndex,
453 source.Length - startIndex,
454 CompareOptions.None));
457 public virtual int IndexOf (string source, string value,
458 CompareOptions options)
460 return(IndexOf (source, value, 0, source.Length,
464 public virtual int IndexOf (string source, string value,
467 return(IndexOf (source, value, startIndex,
468 source.Length - startIndex,
469 CompareOptions.None));
472 public virtual int IndexOf (string source, char value,
474 CompareOptions options)
476 return(IndexOf (source, value, startIndex,
477 source.Length - startIndex, options));
480 public virtual int IndexOf (string source, char value,
481 int startIndex, int count)
483 return IndexOf (source, value, startIndex, count,
484 CompareOptions.None);
487 public virtual int IndexOf (string source, string value,
489 CompareOptions options)
491 return(IndexOf (source, value, startIndex,
492 source.Length - startIndex, options));
495 public virtual int IndexOf (string source, string value,
496 int startIndex, int count)
498 return(IndexOf (source, value, startIndex, count,
499 CompareOptions.None));
503 private int internal_index_managed (string s, int sindex,
504 int count, char c, CompareOptions opt,
508 collator.IndexOf (s, c, sindex, count, opt) :
509 collator.LastIndexOf (s, c, sindex, count, opt);
512 private int internal_index_switch (string s, int sindex,
513 int count, char c, CompareOptions opt,
516 // - forward IndexOf() icall is much faster than
517 // manged version, so always use icall. However,
518 // it does not work for OrdinalIgnoreCase, so
519 // do not avoid managed collator for that option.
520 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
521 internal_index_managed (s, sindex, count, c, opt, first) :
522 internal_index (s, sindex, count, c, opt, first);
525 private int internal_index_switch (string s, int sindex,
526 int count, char c, CompareOptions opt,
530 collator.IndexOf (s, c, sindex, count, opt) :
531 collator.LastIndexOf (s, c, sindex, count, opt);
535 public virtual int IndexOf (string source, char value,
536 int startIndex, int count,
537 CompareOptions options)
540 throw new ArgumentNullException ("source");
543 throw new ArgumentOutOfRangeException ("startIndex");
545 if(count<0 || (source.Length - startIndex) < count) {
546 throw new ArgumentOutOfRangeException ("count");
548 if ((options & ValidCompareOptions_NoStringSort) != options)
549 throw new ArgumentException ("options");
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,
572 private int internal_index_managed (string s1, int sindex,
573 int count, string s2, CompareOptions opt,
577 collator.IndexOf (s1, s2, sindex, count, opt) :
578 collator.LastIndexOf (s1, s2, sindex, count, opt);
581 private int internal_index_switch (string s1, int sindex,
582 int count, string s2, CompareOptions opt,
585 // - forward IndexOf() icall is much faster than
586 // manged version, so always use icall. However,
587 // it does not work for OrdinalIgnoreCase, so
588 // do not avoid managed collator for that option.
589 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
590 internal_index_managed (s1, sindex, count, s2, opt, first) :
591 internal_index (s1, sindex, count, s2, opt, first);
594 private int internal_index_switch (string s1, int sindex,
595 int count, string s2, CompareOptions opt,
599 collator.IndexOf (s1, s2, sindex, count, opt) :
600 collator.LastIndexOf (s1, s2, sindex, count, opt);
604 public virtual int IndexOf (string source, string value,
605 int startIndex, int count,
606 CompareOptions options)
609 throw new ArgumentNullException ("source");
612 throw new ArgumentNullException ("value");
615 throw new ArgumentOutOfRangeException ("startIndex");
617 if(count<0 || (source.Length - startIndex) < count) {
618 throw new ArgumentOutOfRangeException ("count");
620 if ((options & ValidCompareOptions_NoStringSort) != options)
621 throw new ArgumentException ("options");
622 if(value.Length==0) {
629 return (internal_index_switch (source, startIndex, count,
630 value, options, true));
633 public virtual bool IsPrefix(string source, string prefix)
635 return(IsPrefix (source, prefix, CompareOptions.None));
638 public virtual bool IsPrefix(string source, string prefix,
639 CompareOptions options)
642 throw new ArgumentNullException("source");
645 throw new ArgumentNullException("prefix");
648 if ((options & ValidCompareOptions_NoStringSort) != options)
649 throw new ArgumentException ("options");
651 if (UseManagedCollation)
652 return collator.IsPrefix (source, prefix, options);
654 if(source.Length < prefix.Length) {
657 return(Compare (source, 0, prefix.Length,
658 prefix, 0, prefix.Length,
663 public virtual bool IsSuffix(string source, string suffix)
665 return(IsSuffix (source, suffix, CompareOptions.None));
668 public virtual bool IsSuffix(string source, string suffix,
669 CompareOptions options)
672 throw new ArgumentNullException("source");
675 throw new ArgumentNullException("suffix");
678 if ((options & ValidCompareOptions_NoStringSort) != options)
679 throw new ArgumentException ("options");
681 if (UseManagedCollation)
682 return collator.IsSuffix (source, suffix, options);
684 if(source.Length < suffix.Length) {
687 return(Compare (source,
688 source.Length - suffix.Length,
689 suffix.Length, suffix, 0,
690 suffix.Length, options)==0);
694 public virtual int LastIndexOf(string source, char value)
696 return(LastIndexOf (source, value, source.Length - 1,
697 source.Length, CompareOptions.None));
700 public virtual int LastIndexOf(string source, string value)
702 return(LastIndexOf (source, value, source.Length - 1,
703 source.Length, CompareOptions.None));
706 public virtual int LastIndexOf(string source, char value,
707 CompareOptions options)
709 return(LastIndexOf (source, value, source.Length - 1,
710 source.Length, options));
713 public virtual int LastIndexOf(string source, char value,
716 return(LastIndexOf (source, value, startIndex,
718 CompareOptions.None));
721 public virtual int LastIndexOf(string source, string value,
722 CompareOptions options)
724 return(LastIndexOf (source, value, source.Length - 1,
725 source.Length, options));
728 public virtual int LastIndexOf(string source, string value,
731 return(LastIndexOf (source, value, startIndex,
733 CompareOptions.None));
736 public virtual int LastIndexOf(string source, char value,
738 CompareOptions options)
740 return(LastIndexOf (source, value, startIndex,
745 public virtual int LastIndexOf(string source, char value,
746 int startIndex, int count)
748 return(LastIndexOf (source, value, startIndex, count,
749 CompareOptions.None));
752 public virtual int LastIndexOf(string source, string value,
754 CompareOptions options)
756 return(LastIndexOf (source, value, startIndex,
761 public virtual int LastIndexOf(string source, string value,
762 int startIndex, int count)
764 return(LastIndexOf (source, value, startIndex, count,
765 CompareOptions.None));
768 public virtual int LastIndexOf(string source, char value,
769 int startIndex, int count,
770 CompareOptions options)
773 throw new ArgumentNullException("source");
776 throw new ArgumentOutOfRangeException ("startIndex");
778 if(count < 0 || (startIndex - count) < -1) {
779 throw new ArgumentOutOfRangeException("count");
781 if ((options & ValidCompareOptions_NoStringSort) != options)
782 throw new ArgumentException ("options");
788 if((options & CompareOptions.Ordinal)!=0) {
789 for(int pos=startIndex;
790 pos > startIndex - count;
792 if(source[pos]==value) {
798 return (internal_index_switch (source, startIndex,
799 count, value, options,
804 public virtual int LastIndexOf(string source, string value,
805 int startIndex, int count,
806 CompareOptions options)
809 throw new ArgumentNullException("source");
812 throw new ArgumentNullException("value");
815 throw new ArgumentOutOfRangeException ("startIndex");
817 if(count < 0 || (startIndex - count) < -1) {
818 throw new ArgumentOutOfRangeException("count");
820 if ((options & ValidCompareOptions_NoStringSort) != options)
821 throw new ArgumentException ("options");
826 int valuelen=value.Length;
831 return(internal_index_switch (source, startIndex, count,
832 value, options, false));
836 public static bool IsSortable (char ch)
838 return MSCompatUnicodeTable.IsSortable (ch);
842 public static bool IsSortable (string text)
844 return MSCompatUnicodeTable.IsSortable (text);
847 public override string ToString()
849 return("CompareInfo - "+culture);
852 /* LAMESPEC: not mentioned in the spec, but corcompare
853 * shows it. Some documentation about what it does
864 public virtual string Name {
865 get { return icu_name; }