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 public virtual SortKey GetSortKey(string source)
390 return(GetSortKey (source, CompareOptions.None));
393 public virtual SortKey GetSortKey(string source,
394 CompareOptions options)
397 case CompareOptions.Ordinal:
398 case CompareOptions.OrdinalIgnoreCase:
399 throw new ArgumentException ("Now allowed CompareOptions.", "options");
401 if (UseManagedCollation)
402 return collator.GetSortKey (source, options);
403 SortKey key=new SortKey (culture, source, options);
405 /* Need to do the icall here instead of in the
406 * SortKey constructor, as we need access to
407 * this instance's collator.
409 assign_sortkey (key, source, options);
414 public virtual int IndexOf (string source, char value)
416 return(IndexOf (source, value, 0, source.Length,
417 CompareOptions.None));
420 public virtual int IndexOf (string source, string value)
422 return(IndexOf (source, value, 0, source.Length,
423 CompareOptions.None));
426 public virtual int IndexOf (string source, char value,
427 CompareOptions options)
429 return(IndexOf (source, value, 0, source.Length,
433 public virtual int IndexOf (string source, char value,
436 return(IndexOf (source, value, startIndex,
437 source.Length - startIndex,
438 CompareOptions.None));
441 public virtual int IndexOf (string source, string value,
442 CompareOptions options)
444 return(IndexOf (source, value, 0, source.Length,
448 public virtual int IndexOf (string source, string value,
451 return(IndexOf (source, value, startIndex,
452 source.Length - startIndex,
453 CompareOptions.None));
456 public virtual int IndexOf (string source, char value,
458 CompareOptions options)
460 return(IndexOf (source, value, startIndex,
461 source.Length - startIndex, options));
464 public virtual int IndexOf (string source, char value,
465 int startIndex, int count)
467 return IndexOf (source, value, startIndex, count,
468 CompareOptions.None);
471 public virtual int IndexOf (string source, string value,
473 CompareOptions options)
475 return(IndexOf (source, value, startIndex,
476 source.Length - startIndex, options));
479 public virtual int IndexOf (string source, string value,
480 int startIndex, int count)
482 return(IndexOf (source, value, startIndex, count,
483 CompareOptions.None));
486 private int internal_index_managed (string s, int sindex,
487 int count, char c, CompareOptions opt,
491 collator.IndexOf (s, c, sindex, count, opt) :
492 collator.LastIndexOf (s, c, sindex, count, opt);
495 private int internal_index_switch (string s, int sindex,
496 int count, char c, CompareOptions opt,
499 // - forward IndexOf() icall is much faster than
500 // manged version, so always use icall. However,
501 // it does not work for OrdinalIgnoreCase, so
502 // do not avoid managed collator for that option.
503 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
504 internal_index_managed (s, sindex, count, c, opt, first) :
505 internal_index (s, sindex, count, c, opt, first);
508 public virtual int IndexOf (string source, char value,
509 int startIndex, int count,
510 CompareOptions options)
513 throw new ArgumentNullException ("source");
516 throw new ArgumentOutOfRangeException ("startIndex");
518 if(count<0 || (source.Length - startIndex) < count) {
519 throw new ArgumentOutOfRangeException ("count");
521 if ((options & ValidCompareOptions_NoStringSort) != options)
522 throw new ArgumentException ("options");
528 if((options & CompareOptions.Ordinal)!=0) {
529 for(int pos=startIndex;
530 pos < startIndex + count;
532 if(source[pos]==value) {
538 return (internal_index_switch (source, startIndex,
539 count, value, options,
544 private int internal_index_managed (string s1, int sindex,
545 int count, string s2, CompareOptions opt,
549 collator.IndexOf (s1, s2, sindex, count, opt) :
550 collator.LastIndexOf (s1, s2, sindex, count, opt);
553 private int internal_index_switch (string s1, int sindex,
554 int count, string s2, CompareOptions opt,
557 // - forward IndexOf() icall is much faster than
558 // manged version, so always use icall. However,
559 // it does not work for OrdinalIgnoreCase, so
560 // do not avoid managed collator for that option.
561 return UseManagedCollation && ! (first && opt == CompareOptions.Ordinal) ?
562 internal_index_managed (s1, sindex, count, s2, opt, first) :
563 internal_index (s1, sindex, count, s2, opt, first);
566 public virtual int IndexOf (string source, string value,
567 int startIndex, int count,
568 CompareOptions options)
571 throw new ArgumentNullException ("source");
574 throw new ArgumentNullException ("value");
577 throw new ArgumentOutOfRangeException ("startIndex");
579 if(count<0 || (source.Length - startIndex) < count) {
580 throw new ArgumentOutOfRangeException ("count");
582 if ((options & ValidCompareOptions_NoStringSort) != options)
583 throw new ArgumentException ("options");
584 if(value.Length==0) {
591 return (internal_index_switch (source, startIndex, count,
592 value, options, true));
595 public virtual bool IsPrefix(string source, string prefix)
597 return(IsPrefix (source, prefix, CompareOptions.None));
600 public virtual bool IsPrefix(string source, string prefix,
601 CompareOptions options)
604 throw new ArgumentNullException("source");
607 throw new ArgumentNullException("prefix");
610 if ((options & ValidCompareOptions_NoStringSort) != options)
611 throw new ArgumentException ("options");
613 if (UseManagedCollation)
614 return collator.IsPrefix (source, prefix, options);
616 if(source.Length < prefix.Length) {
619 return(Compare (source, 0, prefix.Length,
620 prefix, 0, prefix.Length,
625 public virtual bool IsSuffix(string source, string suffix)
627 return(IsSuffix (source, suffix, CompareOptions.None));
630 public virtual bool IsSuffix(string source, string suffix,
631 CompareOptions options)
634 throw new ArgumentNullException("source");
637 throw new ArgumentNullException("suffix");
640 if ((options & ValidCompareOptions_NoStringSort) != options)
641 throw new ArgumentException ("options");
643 if (UseManagedCollation)
644 return collator.IsSuffix (source, suffix, options);
646 if(source.Length < suffix.Length) {
649 return(Compare (source,
650 source.Length - suffix.Length,
651 suffix.Length, suffix, 0,
652 suffix.Length, options)==0);
656 public virtual int LastIndexOf(string source, char value)
658 return(LastIndexOf (source, value, source.Length - 1,
659 source.Length, CompareOptions.None));
662 public virtual int LastIndexOf(string source, string value)
664 return(LastIndexOf (source, value, source.Length - 1,
665 source.Length, CompareOptions.None));
668 public virtual int LastIndexOf(string source, char value,
669 CompareOptions options)
671 return(LastIndexOf (source, value, source.Length - 1,
672 source.Length, options));
675 public virtual int LastIndexOf(string source, char value,
678 return(LastIndexOf (source, value, startIndex,
680 CompareOptions.None));
683 public virtual int LastIndexOf(string source, string value,
684 CompareOptions options)
686 return(LastIndexOf (source, value, source.Length - 1,
687 source.Length, options));
690 public virtual int LastIndexOf(string source, string value,
693 return(LastIndexOf (source, value, startIndex,
695 CompareOptions.None));
698 public virtual int LastIndexOf(string source, char value,
700 CompareOptions options)
702 return(LastIndexOf (source, value, startIndex,
707 public virtual int LastIndexOf(string source, char value,
708 int startIndex, int count)
710 return(LastIndexOf (source, value, startIndex, count,
711 CompareOptions.None));
714 public virtual int LastIndexOf(string source, string value,
716 CompareOptions options)
718 return(LastIndexOf (source, value, startIndex,
723 public virtual int LastIndexOf(string source, string value,
724 int startIndex, int count)
726 return(LastIndexOf (source, value, startIndex, count,
727 CompareOptions.None));
730 public virtual int LastIndexOf(string source, char value,
731 int startIndex, int count,
732 CompareOptions options)
735 throw new ArgumentNullException("source");
738 throw new ArgumentOutOfRangeException ("startIndex");
740 if(count < 0 || (startIndex - count) < -1) {
741 throw new ArgumentOutOfRangeException("count");
743 if ((options & ValidCompareOptions_NoStringSort) != options)
744 throw new ArgumentException ("options");
750 if((options & CompareOptions.Ordinal)!=0) {
751 for(int pos=startIndex;
752 pos > startIndex - count;
754 if(source[pos]==value) {
760 return (internal_index_switch (source, startIndex,
761 count, value, options,
766 public virtual int LastIndexOf(string source, string value,
767 int startIndex, int count,
768 CompareOptions options)
771 throw new ArgumentNullException("source");
774 throw new ArgumentNullException("value");
777 throw new ArgumentOutOfRangeException ("startIndex");
779 if(count < 0 || (startIndex - count) < -1) {
780 throw new ArgumentOutOfRangeException("count");
782 if ((options & ValidCompareOptions_NoStringSort) != options)
783 throw new ArgumentException ("options");
788 int valuelen=value.Length;
793 return(internal_index_switch (source, startIndex, count,
794 value, options, false));
798 public static bool IsSortable (char ch)
800 return MSCompatUnicodeTable.IsSortable (ch);
804 public static bool IsSortable (string text)
806 return MSCompatUnicodeTable.IsSortable (text);
809 public override string ToString()
811 return("CompareInfo - "+culture);
814 /* LAMESPEC: not mentioned in the spec, but corcompare
815 * shows it. Some documentation about what it does
825 public virtual string Name {
826 get { return m_name; }