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.GetEnvironmentVariable ("MONO_USE_MANAGED_COLLATION")
48 public 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 public virtual int Compare (string string1, int offset1,
245 int length1, string string2,
246 int offset2, int length2,
247 CompareOptions options)
249 /* Not in the spec, but ms does these short
250 * cuts before checking the offsets (breaking
251 * the offset >= string length specified check
254 if((string1.Length == 0 ||
255 offset1 == string1.Length ||
257 (string2.Length == 0 ||
258 offset2 == string2.Length ||
262 if(offset1 < 0 || length1 < 0 ||
263 offset2 < 0 || length2 < 0) {
264 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
267 if(offset1 > string1.Length) {
268 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
271 if(offset2 > string2.Length) {
272 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
275 if(length1 > string1.Length-offset1) {
276 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
279 if(length2 > string2.Length-offset2) {
280 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
283 return(internal_compare_switch (string1, offset1, length1,
284 string2, offset2, length2,
288 public override bool Equals(object value)
290 CompareInfo other=value as CompareInfo;
295 return(other.culture==culture);
298 public static CompareInfo GetCompareInfo(int culture)
300 return(new CultureInfo (culture).CompareInfo);
303 public static CompareInfo GetCompareInfo(string name)
306 throw new ArgumentNullException("name");
308 return(new CultureInfo (name).CompareInfo);
311 public static CompareInfo GetCompareInfo(int culture,
314 /* The assembly parameter is supposedly there
315 * to allow some sort of compare algorithm
318 if(assembly == null) {
319 throw new ArgumentNullException("assembly");
321 if(assembly!=typeof (Object).Module.Assembly) {
322 throw new ArgumentException ("Assembly is an invalid type");
324 return(GetCompareInfo (culture));
327 public static CompareInfo GetCompareInfo(string name,
330 /* The assembly parameter is supposedly there
331 * to allow some sort of compare algorithm
335 throw new ArgumentNullException("name");
337 if(assembly == null) {
338 throw new ArgumentNullException("assembly");
340 if(assembly!=typeof (Object).Module.Assembly) {
341 throw new ArgumentException ("Assembly is an invalid type");
343 return(GetCompareInfo (name));
346 public override int GetHashCode()
351 [MethodImplAttribute (MethodImplOptions.InternalCall)]
352 private extern void assign_sortkey (object key, string source,
353 CompareOptions options);
355 public virtual SortKey GetSortKey(string source)
357 return(GetSortKey (source, CompareOptions.None));
360 public virtual SortKey GetSortKey(string source,
361 CompareOptions options)
363 if (UseManagedCollation)
364 return collator.GetSortKey (source, options);
365 SortKey key=new SortKey (culture, source, options);
367 /* Need to do the icall here instead of in the
368 * SortKey constructor, as we need access to
369 * this instance's collator.
371 assign_sortkey (key, source, options);
376 public virtual int IndexOf (string source, char value)
378 return(IndexOf (source, value, 0, source.Length,
379 CompareOptions.None));
382 public virtual int IndexOf (string source, string value)
384 return(IndexOf (source, value, 0, source.Length,
385 CompareOptions.None));
388 public virtual int IndexOf (string source, char value,
389 CompareOptions options)
391 return(IndexOf (source, value, 0, source.Length,
395 public virtual int IndexOf (string source, char value,
398 return(IndexOf (source, value, startIndex,
399 source.Length - startIndex,
400 CompareOptions.None));
403 public virtual int IndexOf (string source, string value,
404 CompareOptions options)
406 return(IndexOf (source, value, 0, source.Length,
410 public virtual int IndexOf (string source, string value,
413 return(IndexOf (source, value, startIndex,
414 source.Length - startIndex,
415 CompareOptions.None));
418 public virtual int IndexOf (string source, char value,
420 CompareOptions options)
422 return(IndexOf (source, value, startIndex,
423 source.Length - startIndex, options));
426 public virtual int IndexOf (string source, char value,
427 int startIndex, int count)
429 return IndexOf (source, value, startIndex, count,
430 CompareOptions.None);
433 public virtual int IndexOf (string source, string value,
435 CompareOptions options)
437 return(IndexOf (source, value, startIndex,
438 source.Length - startIndex, options));
441 public virtual int IndexOf (string source, string value,
442 int startIndex, int count)
444 return(IndexOf (source, value, startIndex, count,
445 CompareOptions.None));
448 [MethodImplAttribute (MethodImplOptions.InternalCall)]
449 private extern int internal_index (string source, int sindex,
450 int count, char value,
451 CompareOptions options,
454 private int internal_index_managed (string s, int sindex,
455 int count, char c, CompareOptions opt,
459 collator.IndexOf (s, c, sindex, count, opt) :
460 collator.LastIndexOf (s, c, sindex, count, opt);
463 private int internal_index_switch (string s, int sindex,
464 int count, char c, CompareOptions opt,
467 return UseManagedCollation &&
468 (CompareOptions.Ordinal & opt) == 0 ?
469 internal_index_managed (s, sindex, count, c, opt, first) :
470 internal_index (s, sindex, count, c, opt, first);
473 public virtual int IndexOf (string source, char value,
474 int startIndex, int count,
475 CompareOptions options)
478 throw new ArgumentNullException ("source");
481 throw new ArgumentOutOfRangeException ("startIndex");
483 if(count<0 || (source.Length - startIndex) < count) {
484 throw new ArgumentOutOfRangeException ("count");
486 if((options & CompareOptions.StringSort)!=0) {
487 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
494 if((options & CompareOptions.Ordinal)!=0) {
495 for(int pos=startIndex;
496 pos < startIndex + count;
498 if(source[pos]==value) {
504 return (internal_index_switch (source, startIndex,
505 count, value, options,
510 [MethodImplAttribute (MethodImplOptions.InternalCall)]
511 private extern int internal_index (string source, int sindex,
512 int count, string value,
513 CompareOptions options,
516 private int internal_index_managed (string s1, int sindex,
517 int count, string s2, CompareOptions opt,
521 collator.IndexOf (s1, s2, sindex, count, opt) :
522 collator.LastIndexOf (s1, s2, sindex, count, opt);
525 private int internal_index_switch (string s1, int sindex,
526 int count, string s2, CompareOptions opt,
529 return UseManagedCollation &&
530 (CompareOptions.Ordinal & opt) == 0 ?
531 internal_index_managed (s1, sindex, count, s2, opt, first) :
532 internal_index (s1, sindex, count, s2, opt, first);
535 public virtual int IndexOf (string source, string value,
536 int startIndex, int count,
537 CompareOptions options)
540 throw new ArgumentNullException ("source");
543 throw new ArgumentNullException ("value");
546 throw new ArgumentOutOfRangeException ("startIndex");
548 if(count<0 || (source.Length - startIndex) < count) {
549 throw new ArgumentOutOfRangeException ("count");
555 return (internal_index_switch (source, startIndex, count,
556 value, options, true));
559 public virtual bool IsPrefix(string source, string prefix)
561 return(IsPrefix (source, prefix, CompareOptions.None));
564 public virtual bool IsPrefix(string source, string prefix,
565 CompareOptions options)
568 throw new ArgumentNullException("source");
571 throw new ArgumentNullException("prefix");
574 if (UseManagedCollation)
575 return collator.IsPrefix (source, prefix, options);
577 if(source.Length < prefix.Length) {
580 return(Compare (source, 0, prefix.Length,
581 prefix, 0, prefix.Length,
586 public virtual bool IsSuffix(string source, string suffix)
588 return(IsSuffix (source, suffix, CompareOptions.None));
591 public virtual bool IsSuffix(string source, string suffix,
592 CompareOptions options)
595 throw new ArgumentNullException("source");
598 throw new ArgumentNullException("suffix");
601 if (UseManagedCollation)
602 return collator.IsSuffix (source, suffix, options);
604 if(source.Length < suffix.Length) {
607 return(Compare (source,
608 source.Length - suffix.Length,
609 suffix.Length, suffix, 0,
610 suffix.Length, options)==0);
614 public virtual int LastIndexOf(string source, char value)
616 return(LastIndexOf (source, value, source.Length - 1,
617 source.Length, CompareOptions.None));
620 public virtual int LastIndexOf(string source, string value)
622 return(LastIndexOf (source, value, source.Length - 1,
623 source.Length, CompareOptions.None));
626 public virtual int LastIndexOf(string source, char value,
627 CompareOptions options)
629 return(LastIndexOf (source, value, source.Length - 1,
630 source.Length, options));
633 public virtual int LastIndexOf(string source, char value,
636 return(LastIndexOf (source, value, startIndex,
638 CompareOptions.None));
641 public virtual int LastIndexOf(string source, string value,
642 CompareOptions options)
644 return(LastIndexOf (source, value, source.Length - 1,
645 source.Length, options));
648 public virtual int LastIndexOf(string source, string value,
651 return(LastIndexOf (source, value, startIndex,
653 CompareOptions.None));
656 public virtual int LastIndexOf(string source, char value,
658 CompareOptions options)
660 return(LastIndexOf (source, value, startIndex,
665 public virtual int LastIndexOf(string source, char value,
666 int startIndex, int count)
668 return(LastIndexOf (source, value, startIndex, count,
669 CompareOptions.None));
672 public virtual int LastIndexOf(string source, string value,
674 CompareOptions options)
676 return(LastIndexOf (source, value, startIndex,
681 public virtual int LastIndexOf(string source, string value,
682 int startIndex, int count)
684 return(LastIndexOf (source, value, startIndex, count,
685 CompareOptions.None));
688 public virtual int LastIndexOf(string source, char value,
689 int startIndex, int count,
690 CompareOptions options)
693 throw new ArgumentNullException("source");
696 throw new ArgumentOutOfRangeException ("startIndex");
698 if(count < 0 || (startIndex - count) < -1) {
699 throw new ArgumentOutOfRangeException("count");
701 if((options & CompareOptions.StringSort)!=0) {
702 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
709 if((options & CompareOptions.Ordinal)!=0) {
710 for(int pos=startIndex;
711 pos > startIndex - count;
713 if(source[pos]==value) {
719 return (internal_index_switch (source, startIndex,
720 count, value, options,
725 public virtual int LastIndexOf(string source, string value,
726 int startIndex, int count,
727 CompareOptions options)
730 throw new ArgumentNullException("source");
733 throw new ArgumentNullException("value");
736 throw new ArgumentOutOfRangeException ("startIndex");
738 if(count < 0 || (startIndex - count) < -1) {
739 throw new ArgumentOutOfRangeException("count");
745 int valuelen=value.Length;
750 return(internal_index_switch (source, startIndex, count,
751 value, options, false));
754 public override string ToString()
756 return("CompareInfo - "+culture);
759 void IDeserializationCallback.OnDeserialization(object sender)
761 if (UseManagedCollation) {
762 collator = new SimpleCollator (new CultureInfo (culture));
764 /* This will build the ICU collator, and store
765 * the pointer in ICU_collator
768 this.construct_compareinfo (icu_name);
770 ICU_collator=IntPtr.Zero;
775 /* LAMESPEC: not mentioned in the spec, but corcompare
776 * shows it. Some documentation about what it does