fa3e582414c0ecbbbdc40d63e30af6cb3ae957c1
[mono.git] / mcs / class / corlib / System.Globalization / CompareInfo.cs
1 //
2 // System.Globalization.CompareInfo
3 //
4 // Authors:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Dick Porter (dick@ximian.com)
7 //
8 // (C) Ximian, Inc. 2002
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
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:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
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.
32 //
33
34 using System.Reflection;
35 using System.Runtime.Serialization;
36 using System.Runtime.CompilerServices;
37
38 namespace System.Globalization
39 {
40         [Serializable]
41         public class CompareInfo : IDeserializationCallback
42         {
43                 // Keep in synch with MonoCompareInfo in the runtime. 
44                 private int culture;
45                 [NonSerialized]
46                 private string icu_name;
47                 [NonSerialized]
48                 private IntPtr ICU_collator;
49                 private int win32LCID;  // Unused, but MS.NET serializes this
50                 
51                 /* Hide the .ctor() */
52                 CompareInfo() {}
53                 
54                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
55                 private extern void construct_compareinfo (string locale);
56                 
57                 internal CompareInfo (CultureInfo ci)
58                 {
59                         this.culture = ci.LCID;
60                         this.icu_name = ci.IcuName;
61                         this.construct_compareinfo (icu_name);
62                 }
63                 
64                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
65                 private extern void free_internal_collator ();
66                 
67                 ~CompareInfo ()
68                 {
69                         free_internal_collator ();
70                 }
71                 
72                 
73                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
74                 private extern int internal_compare (string str1, int offset1,
75                                                      int length1, string str2,
76                                                      int offset2, int length2,
77                                                      CompareOptions options);
78
79                 public virtual int Compare (string string1, string string2)
80                 {
81                         /* Short cuts... */
82                         if(string1.Length == 0) {
83                                 if(string2.Length == 0) {
84                                         return(0);
85                                 } else {
86                                         return(-1);
87                                 }
88                         } else if(string2.Length == 0) {
89                                 return(1);
90                         }
91
92                         return(internal_compare (string1, 0, string1.Length,
93                                                  string2, 0, string2.Length,
94                                                  CompareOptions.None));
95                 }
96
97                 public virtual int Compare (string string1, string string2,
98                                             CompareOptions options)
99                 {
100                         /* Short cuts... */
101                         if(string1.Length == 0) {
102                                 if(string2.Length == 0) {
103                                         return(0);
104                                 } else {
105                                         return(-1);
106                                 }
107                         } else if(string2.Length == 0) {
108                                 return(1);
109                         }
110
111                         return(internal_compare (string1, 0, string1.Length,
112                                                  string2, 0, string2.Length,
113                                                  options));
114                 }
115
116                 public virtual int Compare (string string1, int offset1,
117                                             string string2, int offset2)
118                 {
119                         /* Not in the spec, but ms does these short
120                          * cuts before checking the offsets (breaking
121                          * the offset >= string length specified check
122                          * in the process...)
123                          */
124                         if(string1.Length == 0 ||
125                            offset1 == string1.Length) {
126                                 if(string2.Length == 0 ||
127                                    offset2 == string2.Length) {
128                                         return(0);
129                                 } else {
130                                         return(-1);
131                                 }
132                         } else if(string2.Length == 0 ||
133                                   offset2 == string2.Length) {
134                                 return(1);
135                         }
136
137                         if(offset1 < 0 || offset2 < 0) {
138                                 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
139                         }
140                         
141                         if(offset1 > string1.Length) {
142                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
143                         }
144                         
145                         if(offset2 > string2.Length) {
146                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
147                         }
148                         
149                         return(internal_compare (string1, offset1,
150                                                  string1.Length-offset1,
151                                                  string2, offset2,
152                                                  string2.Length-offset2,
153                                                  CompareOptions.None));
154                 }
155
156                 public virtual int Compare (string string1, int offset1,
157                                             string string2, int offset2,
158                                             CompareOptions options)
159                 {
160                         /* Not in the spec, but ms does these short
161                          * cuts before checking the offsets (breaking
162                          * the offset >= string length specified check
163                          * in the process...)
164                          */
165                         if(string1.Length == 0 ||
166                            offset1 == string1.Length) {
167                                 if(string2.Length == 0 ||
168                                    offset2 == string2.Length) {
169                                         return(0);
170                                 } else {
171                                         return(-1);
172                                 }
173                         } else if(string2.Length == 0 ||
174                                   offset2 == string2.Length) {
175                                 return(1);
176                         }
177
178                         if(offset1 < 0 || offset2 < 0) {
179                                 throw new ArgumentOutOfRangeException ("Offsets must not be less than zero");
180                         }
181                         
182                         if(offset1 > string1.Length) {
183                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
184                         }
185                         
186                         if(offset2 > string2.Length) {
187                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
188                         }
189                         
190                         return(internal_compare (string1, offset1,
191                                                  string1.Length-offset1,
192                                                  string2, offset2,
193                                                  string2.Length-offset1,
194                                                  options));
195                 }
196
197                 public virtual int Compare (string string1, int offset1,
198                                             int length1, string string2,
199                                             int offset2, int length2)
200                 {
201                         /* Not in the spec, but ms does these short
202                          * cuts before checking the offsets (breaking
203                          * the offset >= string length specified check
204                          * in the process...)
205                          */
206                         if(string1.Length == 0 ||
207                            offset1 == string1.Length ||
208                            length1 == 0) {
209                                 if(string2.Length == 0 ||
210                                    offset2 == string2.Length ||
211                                    length2 == 0) {
212                                         return(0);
213                                 } else {
214                                         return(-1);
215                                 }
216                         } else if(string2.Length == 0 ||
217                                   offset2 == string2.Length ||
218                                   length2 == 0) {
219                                 return(1);
220                         }
221
222                         if(offset1 < 0 || length1 < 0 ||
223                            offset2 < 0 || length2 < 0) {
224                                 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
225                         }
226                         
227                         if(offset1 > string1.Length) {
228                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
229                         }
230                         
231                         if(offset2 > string2.Length) {
232                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
233                         }
234                         
235                         if(length1 > string1.Length-offset1) {
236                                 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
237                         }
238                         
239                         if(length2 > string2.Length-offset2) {
240                                 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
241                         }
242                         
243                         return(internal_compare (string1, offset1, length1,
244                                                  string2, offset2, length2,
245                                                  CompareOptions.None));
246                 }
247
248                 public virtual int Compare (string string1, int offset1,
249                                             int length1, string string2,
250                                             int offset2, int length2,
251                                             CompareOptions options)
252                 {
253                         /* Not in the spec, but ms does these short
254                          * cuts before checking the offsets (breaking
255                          * the offset >= string length specified check
256                          * in the process...)
257                          */
258                         if(string1.Length == 0 ||
259                            offset1 == string1.Length ||
260                            length1 == 0) {
261                                 if(string2.Length == 0 ||
262                                    offset2 == string2.Length ||
263                                    length2 == 0) {
264                                         return(0);
265                                 } else {
266                                         return(-1);
267                                 }
268                         } else if(string2.Length == 0 ||
269                                   offset2 == string2.Length ||
270                                   length2 == 0) {
271                                 return(1);
272                         }
273
274                         if(offset1 < 0 || length1 < 0 ||
275                            offset2 < 0 || length2 < 0) {
276                                 throw new ArgumentOutOfRangeException ("Offsets and lengths must not be less than zero");
277                         }
278                         
279                         if(offset1 > string1.Length) {
280                                 throw new ArgumentOutOfRangeException ("Offset1 is greater than or equal to the length of string1");
281                         }
282                         
283                         if(offset2 > string2.Length) {
284                                 throw new ArgumentOutOfRangeException ("Offset2 is greater than or equal to the length of string2");
285                         }
286                         
287                         if(length1 > string1.Length-offset1) {
288                                 throw new ArgumentOutOfRangeException ("Length1 is greater than the number of characters from offset1 to the end of string1");
289                         }
290                         
291                         if(length2 > string2.Length-offset2) {
292                                 throw new ArgumentOutOfRangeException ("Length2 is greater than the number of characters from offset2 to the end of string2");
293                         }
294                         
295                         return(internal_compare (string1, offset1, length1,
296                                                  string2, offset2, length2,
297                                                  options));
298                 }
299
300                 public override bool Equals(object value)
301                 {
302                         CompareInfo other=value as CompareInfo;
303                         if(other==null) {
304                                 return(false);
305                         }
306                         
307                         return(other.culture==culture);
308                 }
309
310                 public static CompareInfo GetCompareInfo(int culture)
311                 {
312                         return(new CultureInfo (culture).CompareInfo);
313                 }
314
315                 public static CompareInfo GetCompareInfo(string name)
316                 {
317                         if(name == null) {
318                                 throw new ArgumentNullException("name");
319                         }
320                         return(new CultureInfo (name).CompareInfo);
321                 }
322
323                 public static CompareInfo GetCompareInfo(int culture,
324                                                          Assembly assembly)
325                 {
326                         /* The assembly parameter is supposedly there
327                          * to allow some sort of compare algorithm
328                          * versioning.
329                          */
330                         if(assembly == null) {
331                                 throw new ArgumentNullException("assembly");
332                         }
333                         if(assembly!=typeof (Object).Module.Assembly) {
334                                 throw new ArgumentException ("Assembly is an invalid type");
335                         }
336                         return(GetCompareInfo (culture));
337                 }
338
339                 public static CompareInfo GetCompareInfo(string name,
340                                                          Assembly assembly)
341                 {
342                         /* The assembly parameter is supposedly there
343                          * to allow some sort of compare algorithm
344                          * versioning.
345                          */
346                         if(name == null) {
347                                 throw new ArgumentNullException("name");
348                         }
349                         if(assembly == null) {
350                                 throw new ArgumentNullException("assembly");
351                         }
352                         if(assembly!=typeof (Object).Module.Assembly) {
353                                 throw new ArgumentException ("Assembly is an invalid type");
354                         }
355                         return(GetCompareInfo (name));
356                 }
357
358                 public override int GetHashCode()
359                 {
360                         return(LCID);
361                 }
362
363                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
364                 private extern void assign_sortkey (object key, string source,
365                                                     CompareOptions options);
366                 
367                 public virtual SortKey GetSortKey(string source)
368                 {
369                         return(GetSortKey (source, CompareOptions.None));
370                 }
371
372                 public virtual SortKey GetSortKey(string source,
373                                                   CompareOptions options)
374                 {
375                         SortKey key=new SortKey (culture, source, options);
376
377                         /* Need to do the icall here instead of in the
378                          * SortKey constructor, as we need access to
379                          * this instance's collator.
380                          */
381                         assign_sortkey (key, source, options);
382                         
383                         return(key);
384                 }
385
386                 public virtual int IndexOf (string source, char value)
387                 {
388                         return(IndexOf (source, value, 0, source.Length,
389                                         CompareOptions.None));
390                 }
391
392                 public virtual int IndexOf (string source, string value)
393                 {
394                         return(IndexOf (source, value, 0, source.Length,
395                                         CompareOptions.None));
396                 }
397
398                 public virtual int IndexOf (string source, char value,
399                                             CompareOptions options)
400                 {
401                         return(IndexOf (source, value, 0, source.Length,
402                                         options));
403                 }
404
405                 public virtual int IndexOf (string source, char value,
406                                             int startIndex)
407                 {
408                         return(IndexOf (source, value, startIndex,
409                                         source.Length - startIndex,
410                                         CompareOptions.None));
411                 }
412                 
413                 public virtual int IndexOf (string source, string value,
414                                             CompareOptions options)
415                 {
416                         return(IndexOf (source, value, 0, source.Length,
417                                         options));
418                 }
419
420                 public virtual int IndexOf (string source, string value,
421                                             int startIndex)
422                 {
423                         return(IndexOf (source, value, startIndex,
424                                         source.Length - startIndex,
425                                         CompareOptions.None));
426                 }
427
428                 public virtual int IndexOf (string source, char value,
429                                             int startIndex,
430                                             CompareOptions options)
431                 {
432                         return(IndexOf (source, value, startIndex,
433                                         source.Length - startIndex, options));
434                 }
435
436                 public virtual int IndexOf (string source, char value,
437                                             int startIndex, int count)
438                 {
439                         return IndexOf (source, value, startIndex, count,
440                                         CompareOptions.None);
441                 }
442
443                 public virtual int IndexOf (string source, string value,
444                                             int startIndex,
445                                             CompareOptions options)
446                 {
447                         return(IndexOf (source, value, startIndex,
448                                         source.Length - startIndex, options));
449                 }
450
451                 public virtual int IndexOf (string source, string value,
452                                             int startIndex, int count)
453                 {
454                         return(IndexOf (source, value, startIndex, count,
455                                         CompareOptions.None));
456                 }
457
458                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
459                 private extern int internal_index (string source, int sindex,
460                                                    int count, char value,
461                                                    CompareOptions options,
462                                                    bool first);
463                 
464                 public virtual int IndexOf (string source, char value,
465                                             int startIndex, int count,
466                                             CompareOptions options)
467                 {
468                         if(source==null) {
469                                 throw new ArgumentNullException ("source");
470                         }
471                         if(startIndex<0) {
472                                 throw new ArgumentOutOfRangeException ("startIndex");
473                         }
474                         if(count<0 || (source.Length - startIndex) < count) {
475                                 throw new ArgumentOutOfRangeException ("count");
476                         }
477                         if((options & CompareOptions.StringSort)!=0) {
478                                 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
479                         }
480                         
481                         if(count==0) {
482                                 return(-1);
483                         }
484
485                         if((options & CompareOptions.Ordinal)!=0) {
486                                 for(int pos=startIndex;
487                                     pos < startIndex + count;
488                                     pos++) {
489                                         if(source[pos]==value) {
490                                                 return(pos);
491                                         }
492                                 }
493                                 return(-1);
494                         } else {
495                                 return (internal_index (source, startIndex,
496                                                         count, value, options,
497                                                         true));
498                         }
499                 }
500
501                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
502                 private extern int internal_index (string source, int sindex,
503                                                    int count, string value,
504                                                    CompareOptions options,
505                                                    bool first);
506                 
507                 public virtual int IndexOf (string source, string value,
508                                             int startIndex, int count,
509                                             CompareOptions options)
510                 {
511                         if(source==null) {
512                                 throw new ArgumentNullException ("source");
513                         }
514                         if(value==null) {
515                                 throw new ArgumentNullException ("value");
516                         }
517                         if(startIndex<0) {
518                                 throw new ArgumentOutOfRangeException ("startIndex");
519                         }
520                         if(count<0 || (source.Length - startIndex) < count) {
521                                 throw new ArgumentOutOfRangeException ("count");
522                         }
523                         if(count==0) {
524                                 return(-1);
525                         }
526
527                         return (internal_index (source, startIndex, count,
528                                                 value, options, true));
529                 }
530
531                 public virtual bool IsPrefix(string source, string prefix)
532                 {
533                         return(IsPrefix (source, prefix, CompareOptions.None));
534                 }
535
536                 public virtual bool IsPrefix(string source, string prefix,
537                                              CompareOptions options)
538                 {
539                         if(source == null) {
540                                 throw new ArgumentNullException("source");
541                         }
542                         if(prefix == null) {
543                                 throw new ArgumentNullException("prefix");
544                         }
545
546                         if(source.Length < prefix.Length) {
547                                 return(false);
548                         } else {
549                                 return(Compare (source, 0, prefix.Length,
550                                                 prefix, 0, prefix.Length,
551                                                 options)==0);
552                         }
553                 }
554
555                 public virtual bool IsSuffix(string source, string suffix)
556                 {
557                         return(IsSuffix (source, suffix, CompareOptions.None));
558                 }
559
560                 public virtual bool IsSuffix(string source, string suffix,
561                                              CompareOptions options)
562                 {
563                         if(source == null) {
564                                 throw new ArgumentNullException("source");
565                         }
566                         if(suffix == null) {
567                                 throw new ArgumentNullException("suffix");
568                         }
569
570                         if(source.Length < suffix.Length) {
571                                 return(false);
572                         } else {
573                                 return(Compare (source,
574                                                 source.Length - suffix.Length,
575                                                 suffix.Length, suffix, 0,
576                                                 suffix.Length, options)==0);
577                         }
578                 }
579
580                 public virtual int LastIndexOf(string source, char value)
581                 {
582                         return(LastIndexOf (source, value, source.Length - 1,
583                                             source.Length, CompareOptions.None));
584                 }
585
586                 public virtual int LastIndexOf(string source, string value)
587                 {
588                         return(LastIndexOf (source, value, source.Length - 1,
589                                             source.Length, CompareOptions.None));
590                 }
591
592                 public virtual int LastIndexOf(string source, char value,
593                                                CompareOptions options)
594                 {
595                         return(LastIndexOf (source, value, source.Length - 1,
596                                             source.Length, options));
597                 }
598
599                 public virtual int LastIndexOf(string source, char value,
600                                                int startIndex)
601                 {
602                         return(LastIndexOf (source, value, startIndex,
603                                             startIndex + 1,
604                                             CompareOptions.None));
605                 }
606
607                 public virtual int LastIndexOf(string source, string value,
608                                                CompareOptions options)
609                 {
610                         return(LastIndexOf (source, value, source.Length - 1,
611                                             source.Length, options));
612                 }
613
614                 public virtual int LastIndexOf(string source, string value,
615                                                int startIndex)
616                 {
617                         return(LastIndexOf (source, value, startIndex,
618                                             startIndex + 1,
619                                             CompareOptions.None));
620                 }
621
622                 public virtual int LastIndexOf(string source, char value,
623                                                int startIndex,
624                                                CompareOptions options)
625                 {
626                         return(LastIndexOf (source, value, startIndex,
627                                             startIndex + 1,
628                                             options));
629                 }
630
631                 public virtual int LastIndexOf(string source, char value,
632                                                int startIndex, int count)
633                 {
634                         return(LastIndexOf (source, value, startIndex, count,
635                                             CompareOptions.None));
636                 }
637
638                 public virtual int LastIndexOf(string source, string value,
639                                                int startIndex,
640                                                CompareOptions options)
641                 {
642                         return(LastIndexOf (source, value, startIndex,
643                                             startIndex + 1,
644                                             options));
645                 }
646
647                 public virtual int LastIndexOf(string source, string value,
648                                                int startIndex, int count)
649                 {
650                         return(LastIndexOf (source, value, startIndex, count,
651                                             CompareOptions.None));
652                 }
653
654                 public virtual int LastIndexOf(string source, char value,
655                                                int startIndex, int count,
656                                                CompareOptions options)
657                 {
658                         if(source == null) {
659                                 throw new ArgumentNullException("source");
660                         }
661                         if(startIndex < 0) {
662                                 throw new ArgumentOutOfRangeException ("startIndex");
663                         }
664                         if(count < 0 || (startIndex - count) < -1) {
665                                 throw new ArgumentOutOfRangeException("count");
666                         }
667                         if((options & CompareOptions.StringSort)!=0) {
668                                 throw new ArgumentException ("StringSort is not a valid CompareOption for this method");
669                         }
670                         
671                         if(count==0) {
672                                 return(-1);
673                         }
674
675                         if((options & CompareOptions.Ordinal)!=0) {
676                                 for(int pos=startIndex;
677                                     pos > startIndex - count;
678                                     pos--) {
679                                         if(source[pos]==value) {
680                                                 return(pos);
681                                         }
682                                 }
683                                 return(-1);
684                         } else {
685                                 return (internal_index (source, startIndex,
686                                                         count, value, options,
687                                                         false));
688                         }
689                 }
690
691                 public virtual int LastIndexOf(string source, string value,
692                                                int startIndex, int count,
693                                                CompareOptions options)
694                 {
695                         if(source == null) {
696                                 throw new ArgumentNullException("source");
697                         }
698                         if(value == null) {
699                                 throw new ArgumentNullException("value");
700                         }
701                         if(startIndex < 0) {
702                                 throw new ArgumentOutOfRangeException ("startIndex");
703                         }
704                         if(count < 0 || (startIndex - count) < -1) {
705                                 throw new ArgumentOutOfRangeException("count");
706                         }
707                         if(count == 0) {
708                                 return(-1);
709                         }
710
711                         int valuelen=value.Length;
712                         if(valuelen==0) {
713                                 return(0);
714                         }
715
716                         return(internal_index (source, startIndex, count,
717                                                value, options, false));
718                 }
719
720                 public override string ToString()
721                 {
722                         return("CompareInfo - "+culture);
723                 }
724
725                 void IDeserializationCallback.OnDeserialization(object sender)
726                 {
727                         /* This will build the ICU collator, and store
728                          * the pointer in ICU_collator
729                          */
730                         try {
731                                 this.construct_compareinfo (icu_name);
732                         } catch {
733                                 ICU_collator=IntPtr.Zero;
734                         }
735                 }
736
737                 /* LAMESPEC: not mentioned in the spec, but corcompare
738                  * shows it.  Some documentation about what it does
739                  * would be nice.
740                  */
741                 public int LCID
742                 {
743                         get {
744                                 return(culture);
745                         }
746                 }
747         }
748 }