2 // System.Globalization.CompareInfo
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.Runtime.Serialization;
36 using System.Runtime.CompilerServices;
37 using Mono.Globalization.Unicode;
39 namespace System.Globalization
42 public class CompareInfo : IDeserializationCallback
44 static readonly bool useManagedCollation =
45 Environment.internalGetEnvironmentVariable ("MONO_DISABLE_MANAGED_COLLATION")
48 internal static bool UseManagedCollation {
49 get { return useManagedCollation && MSCompatUnicodeTable.IsReady; }
52 // Keep in synch with MonoCompareInfo in the runtime.
55 private string icu_name;
57 private IntPtr ICU_collator;
58 private int win32LCID; // Unused, but MS.NET serializes this
61 SimpleCollator collator;
63 /* Hide the .ctor() */
66 [MethodImplAttribute (MethodImplOptions.InternalCall)]
67 private extern void construct_compareinfo (string locale);
69 internal CompareInfo (CultureInfo ci)
71 this.culture = ci.LCID;
72 if (UseManagedCollation)
73 collator = new SimpleCollator (ci);
75 this.icu_name = ci.IcuName;
76 this.construct_compareinfo (icu_name);
80 [MethodImplAttribute (MethodImplOptions.InternalCall)]
81 private extern void free_internal_collator ();
85 free_internal_collator ();
88 [MethodImplAttribute (MethodImplOptions.InternalCall)]
89 private extern int internal_compare (string str1, int offset1,
90 int length1, string str2,
91 int offset2, int length2,
92 CompareOptions options);
94 private int internal_compare_managed (string str1, int offset1,
95 int length1, string str2,
96 int offset2, int length2,
97 CompareOptions options)
99 return collator.Compare (str1, offset1, length1,
100 str2, offset2, length2, options);
103 private int internal_compare_switch (string str1, int offset1,
104 int length1, string str2,
105 int offset2, int length2,
106 CompareOptions options)
108 return UseManagedCollation ?
109 internal_compare_managed (str1, offset1, length1,
110 str2, offset2, length2, options) :
111 internal_compare (str1, offset1, length1,
112 str2, offset2, length2, options);
115 public virtual int Compare (string string1, string string2)
118 if(string1.Length == 0 && string2.Length == 0)
121 return(internal_compare_switch (string1, 0, string1.Length,
122 string2, 0, string2.Length,
123 CompareOptions.None));
126 public virtual int Compare (string string1, string string2,
127 CompareOptions options)
130 if(string1.Length == 0 && string2.Length == 0)
133 return(internal_compare_switch (string1, 0, string1.Length,
134 string2, 0, string2.Length,
138 public virtual int Compare (string string1, int offset1,
139 string string2, int offset2)
141 /* Not in the spec, but ms does these short
142 * cuts before checking the offsets (breaking
143 * the offset >= string length specified check
146 if ((string1.Length == 0 || offset1 == string1.Length) &&
147 (string2.Length == 0 || offset2 == string2.Length))
150 if(offset1 < 0 || offset2 < 0) {
151 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
154 if(offset1 > string1.Length) {
155 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
158 if(offset2 > string2.Length) {
159 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
162 return(internal_compare_switch (string1, offset1,
163 string1.Length-offset1,
165 string2.Length-offset2,
166 CompareOptions.None));
169 public virtual int Compare (string string1, int offset1,
170 string string2, int offset2,
171 CompareOptions options)
173 /* Not in the spec, but ms does these short
174 * cuts before checking the offsets (breaking
175 * the offset >= string length specified check
178 if((string1.Length == 0 || offset1 == string1.Length) &&
179 (string2.Length == 0 || offset2 == string2.Length))
182 if(offset1 < 0 || offset2 < 0) {
183 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
186 if(offset1 > string1.Length) {
187 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
190 if(offset2 > string2.Length) {
191 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
194 return(internal_compare_switch (string1, offset1,
195 string1.Length-offset1,
197 string2.Length-offset1,
201 public virtual int Compare (string string1, int offset1,
202 int length1, string string2,
203 int offset2, int length2)
205 /* Not in the spec, but ms does these short
206 * cuts before checking the offsets (breaking
207 * the offset >= string length specified check
210 if((string1.Length == 0 ||
211 offset1 == string1.Length ||
213 (string2.Length == 0 ||
214 offset2 == string2.Length ||
218 if(offset1 < 0 || length1 < 0 ||
219 offset2 < 0 || length2 < 0) {
220 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
223 if(offset1 > string1.Length) {
224 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
227 if(offset2 > string2.Length) {
228 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
231 if(length1 > string1.Length-offset1) {
232 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
235 if(length2 > string2.Length-offset2) {
236 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
239 return(internal_compare_switch (string1, offset1, length1,
240 string2, offset2, length2,
241 CompareOptions.None));
244 [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
245 public virtual int Compare (string string1, int offset1,
246 int length1, string string2,
247 int offset2, int length2,
248 CompareOptions options)
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 ||
256 offset1 == string1.Length ||
258 (string2.Length == 0 ||
259 offset2 == string2.Length ||
263 if(offset1 < 0 || length1 < 0 ||
264 offset2 < 0 || length2 < 0) {
265 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
268 if(offset1 > string1.Length) {
269 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
272 if(offset2 > string2.Length) {
273 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
276 if(length1 > string1.Length-offset1) {
277 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
280 if(length2 > string2.Length-offset2) {
281 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
284 return(internal_compare_switch (string1, offset1, length1,
285 string2, offset2, length2,
289 public override bool Equals(object value)
291 CompareInfo other=value as CompareInfo;
296 return(other.culture==culture);
299 public static CompareInfo GetCompareInfo(int culture)
301 return(new CultureInfo (culture).CompareInfo);
304 public static CompareInfo GetCompareInfo(string name)
307 throw new ArgumentNullException("name");
309 return(new CultureInfo (name).CompareInfo);
312 public static CompareInfo GetCompareInfo(int culture,
315 /* The assembly parameter is supposedly there
316 * to allow some sort of compare algorithm
319 if(assembly == null) {
320 throw new ArgumentNullException("assembly");
322 if(assembly!=typeof (Object).Module.Assembly) {
323 throw new ArgumentException ("Assembly is an invalid type");
325 return(GetCompareInfo (culture));
328 public static CompareInfo GetCompareInfo(string name,
331 /* The assembly parameter is supposedly there
332 * to allow some sort of compare algorithm
336 throw new ArgumentNullException("name");
338 if(assembly == null) {
339 throw new ArgumentNullException("assembly");
341 if(assembly!=typeof (Object).Module.Assembly) {
342 throw new ArgumentException ("Assembly is an invalid type");
344 return(GetCompareInfo (name));
347 public override int GetHashCode()
352 [MethodImplAttribute (MethodImplOptions.InternalCall)]
353 private extern void assign_sortkey (object key, string source,
354 CompareOptions options);
356 public virtual SortKey GetSortKey(string source)
358 return(GetSortKey (source, CompareOptions.None));
361 public virtual SortKey GetSortKey(string source,
362 CompareOptions options)
364 if (UseManagedCollation)
365 return collator.GetSortKey (source, options);
366 SortKey key=new SortKey (culture, source, options);
368 /* Need to do the icall here instead of in the
369 * SortKey constructor, as we need access to
370 * this instance's collator.
372 assign_sortkey (key, source, options);
377 public virtual int IndexOf (string source, char value)
379 return(IndexOf (source, value, 0, source.Length,
380 CompareOptions.None));
383 public virtual int IndexOf (string source, string value)
385 return(IndexOf (source, value, 0, source.Length,
386 CompareOptions.None));
389 public virtual int IndexOf (string source, char value,
390 CompareOptions options)
392 return(IndexOf (source, value, 0, source.Length,
396 public virtual int IndexOf (string source, char value,
399 return(IndexOf (source, value, startIndex,
400 source.Length - startIndex,
401 CompareOptions.None));
404 public virtual int IndexOf (string source, string value,
405 CompareOptions options)
407 return(IndexOf (source, value, 0, source.Length,
411 public virtual int IndexOf (string source, string value,
414 return(IndexOf (source, value, startIndex,
415 source.Length - startIndex,
416 CompareOptions.None));
419 public virtual int IndexOf (string source, char value,
421 CompareOptions options)
423 return(IndexOf (source, value, startIndex,
424 source.Length - startIndex, options));
427 public virtual int IndexOf (string source, char value,
428 int startIndex, int count)
430 return IndexOf (source, value, startIndex, count,
431 CompareOptions.None);
434 public virtual int IndexOf (string source, string value,
436 CompareOptions options)
438 return(IndexOf (source, value, startIndex,
439 source.Length - startIndex, options));
442 public virtual int IndexOf (string source, string value,
443 int startIndex, int count)
445 return(IndexOf (source, value, startIndex, count,
446 CompareOptions.None));
449 [MethodImplAttribute (MethodImplOptions.InternalCall)]
450 private extern int internal_index (string source, int sindex,
451 int count, char value,
452 CompareOptions options,
455 private int internal_index_managed (string s, int sindex,
456 int count, char c, CompareOptions opt,
460 collator.IndexOf (s, c, sindex, count, opt) :
461 collator.LastIndexOf (s, c, sindex, count, opt);
464 private int internal_index_switch (string s, int sindex,
465 int count, char c, CompareOptions opt,
468 return UseManagedCollation &&
470 ((CompareOptions.Ordinal & opt) == 0 ||
471 (CompareOptions.OrdinalIgnoreCase & opt) == 0) ?
473 (CompareOptions.Ordinal & opt) == 0 ?
475 internal_index_managed (s, sindex, count, c, opt, first) :
476 internal_index (s, sindex, count, c, opt, first);
479 [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
480 public virtual int IndexOf (string source, char value,
481 int startIndex, int count,
482 CompareOptions options)
485 throw new ArgumentNullException ("source");
488 throw new ArgumentOutOfRangeException ("startIndex");
490 if(count<0 || (source.Length - startIndex) < count) {
491 throw new ArgumentOutOfRangeException ("count");
493 if((options & CompareOptions.StringSort)!=0) {
494 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
501 if((options & CompareOptions.Ordinal)!=0) {
502 for(int pos=startIndex;
503 pos < startIndex + count;
505 if(source[pos]==value) {
511 return (internal_index_switch (source, startIndex,
512 count, value, options,
517 [MethodImplAttribute (MethodImplOptions.InternalCall)]
518 private extern int internal_index (string source, int sindex,
519 int count, string value,
520 CompareOptions options,
523 private int internal_index_managed (string s1, int sindex,
524 int count, string s2, CompareOptions opt,
528 collator.IndexOf (s1, s2, sindex, count, opt) :
529 collator.LastIndexOf (s1, s2, sindex, count, opt);
532 private int internal_index_switch (string s1, int sindex,
533 int count, string s2, CompareOptions opt,
536 return UseManagedCollation &&
538 ((CompareOptions.Ordinal & opt) == 0 ||
539 (CompareOptions.OrdinalIgnoreCase & opt) == 0) ?
541 (CompareOptions.Ordinal & opt) == 0 ?
543 internal_index_managed (s1, sindex, count, s2, opt, first) :
544 internal_index (s1, sindex, count, s2, opt, first);
547 [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
548 public virtual int IndexOf (string source, string value,
549 int startIndex, int count,
550 CompareOptions options)
553 throw new ArgumentNullException ("source");
556 throw new ArgumentNullException ("value");
559 throw new ArgumentOutOfRangeException ("startIndex");
561 if(count<0 || (source.Length - startIndex) < count) {
562 throw new ArgumentOutOfRangeException ("count");
568 return (internal_index_switch (source, startIndex, count,
569 value, options, true));
572 public virtual bool IsPrefix(string source, string prefix)
574 return(IsPrefix (source, prefix, CompareOptions.None));
577 public virtual bool IsPrefix(string source, string prefix,
578 CompareOptions options)
581 throw new ArgumentNullException("source");
584 throw new ArgumentNullException("prefix");
587 if (UseManagedCollation)
588 return collator.IsPrefix (source, prefix, options);
590 if(source.Length < prefix.Length) {
593 return(Compare (source, 0, prefix.Length,
594 prefix, 0, prefix.Length,
599 public virtual bool IsSuffix(string source, string suffix)
601 return(IsSuffix (source, suffix, CompareOptions.None));
604 public virtual bool IsSuffix(string source, string suffix,
605 CompareOptions options)
608 throw new ArgumentNullException("source");
611 throw new ArgumentNullException("suffix");
614 if (UseManagedCollation)
615 return collator.IsSuffix (source, suffix, options);
617 if(source.Length < suffix.Length) {
620 return(Compare (source,
621 source.Length - suffix.Length,
622 suffix.Length, suffix, 0,
623 suffix.Length, options)==0);
627 public virtual int LastIndexOf(string source, char value)
629 return(LastIndexOf (source, value, source.Length - 1,
630 source.Length, CompareOptions.None));
633 public virtual int LastIndexOf(string source, string value)
635 return(LastIndexOf (source, value, source.Length - 1,
636 source.Length, CompareOptions.None));
639 public virtual int LastIndexOf(string source, char value,
640 CompareOptions options)
642 return(LastIndexOf (source, value, source.Length - 1,
643 source.Length, options));
646 public virtual int LastIndexOf(string source, char value,
649 return(LastIndexOf (source, value, startIndex,
651 CompareOptions.None));
654 public virtual int LastIndexOf(string source, string value,
655 CompareOptions options)
657 return(LastIndexOf (source, value, source.Length - 1,
658 source.Length, options));
661 public virtual int LastIndexOf(string source, string value,
664 return(LastIndexOf (source, value, startIndex,
666 CompareOptions.None));
669 public virtual int LastIndexOf(string source, char value,
671 CompareOptions options)
673 return(LastIndexOf (source, value, startIndex,
678 public virtual int LastIndexOf(string source, char value,
679 int startIndex, int count)
681 return(LastIndexOf (source, value, startIndex, count,
682 CompareOptions.None));
685 public virtual int LastIndexOf(string source, string value,
687 CompareOptions options)
689 return(LastIndexOf (source, value, startIndex,
694 public virtual int LastIndexOf(string source, string value,
695 int startIndex, int count)
697 return(LastIndexOf (source, value, startIndex, count,
698 CompareOptions.None));
701 [MonoTODO("Add support for CompareOptions.OrdinalIgnoreCase")]
702 public virtual int LastIndexOf(string source, char value,
703 int startIndex, int count,
704 CompareOptions options)
707 throw new ArgumentNullException("source");
710 throw new ArgumentOutOfRangeException ("startIndex");
712 if(count < 0 || (startIndex - count) < -1) {
713 throw new ArgumentOutOfRangeException("count");
715 if((options & CompareOptions.StringSort)!=0) {
716 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
723 if((options & CompareOptions.Ordinal)!=0) {
724 for(int pos=startIndex;
725 pos > startIndex - count;
727 if(source[pos]==value) {
733 return (internal_index_switch (source, startIndex,
734 count, value, options,
739 public virtual int LastIndexOf(string source, string value,
740 int startIndex, int count,
741 CompareOptions options)
744 throw new ArgumentNullException("source");
747 throw new ArgumentNullException("value");
750 throw new ArgumentOutOfRangeException ("startIndex");
752 if(count < 0 || (startIndex - count) < -1) {
753 throw new ArgumentOutOfRangeException("count");
759 int valuelen=value.Length;
764 return(internal_index_switch (source, startIndex, count,
765 value, options, false));
769 public static bool IsSortable (char c)
771 return MSCompatUnicodeTable.IsSortable (c);
774 public static bool IsSortable (string s)
776 return MSCompatUnicodeTable.IsSortable (s);
780 public override string ToString()
782 return("CompareInfo - "+culture);
785 void IDeserializationCallback.OnDeserialization(object sender)
787 if (UseManagedCollation) {
788 collator = new SimpleCollator (new CultureInfo (culture));
790 /* This will build the ICU collator, and store
791 * the pointer in ICU_collator
794 this.construct_compareinfo (icu_name);
796 ICU_collator=IntPtr.Zero;
801 /* LAMESPEC: not mentioned in the spec, but corcompare
802 * shows it. Some documentation about what it does