Bump corefx
[mono.git] / mcs / class / referencesource / mscorlib / system / stringcomparer.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 namespace System {
8     using System.Collections;
9     using System.Collections.Generic;    
10     using System.Globalization;
11     using System.Diagnostics.Contracts;
12     using System.Runtime.Serialization;
13     using System.Runtime.CompilerServices;
14
15
16     [Serializable]
17     [System.Runtime.InteropServices.ComVisible(true)]
18     public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>{
19         private static readonly StringComparer _invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);        
20         private static readonly StringComparer _invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);      
21         private static readonly StringComparer _ordinal = new OrdinalComparer(false);
22         private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true);        
23
24         public static StringComparer InvariantCulture { 
25             get {
26                 Contract.Ensures(Contract.Result<StringComparer>() != null);
27                 return _invariantCulture;
28             }
29         }
30         
31         public static StringComparer InvariantCultureIgnoreCase { 
32             get {
33                 Contract.Ensures(Contract.Result<StringComparer>() != null);
34                 return _invariantCultureIgnoreCase;
35             }
36         }
37
38         public static StringComparer CurrentCulture { 
39             get {
40                 Contract.Ensures(Contract.Result<StringComparer>() != null);
41                 return new CultureAwareComparer(CultureInfo.CurrentCulture, false);                
42             }
43         }
44         
45         public static StringComparer CurrentCultureIgnoreCase { 
46             get {
47                 Contract.Ensures(Contract.Result<StringComparer>() != null);
48                 return new CultureAwareComparer(CultureInfo.CurrentCulture, true);                
49             }
50         }
51
52         public static StringComparer Ordinal { 
53             get {
54                 Contract.Ensures(Contract.Result<StringComparer>() != null);
55                 return _ordinal;
56             }
57         }
58
59         public static StringComparer OrdinalIgnoreCase { 
60             get {
61                 Contract.Ensures(Contract.Result<StringComparer>() != null);
62                 return _ordinalIgnoreCase;
63             }
64         }
65
66         public static StringComparer Create(CultureInfo culture, bool ignoreCase) {
67             if( culture == null) {
68                 throw new ArgumentNullException("culture");
69             }
70             Contract.Ensures(Contract.Result<StringComparer>() != null);
71             Contract.EndContractBlock();
72             
73             return new CultureAwareComparer(culture, ignoreCase);            
74         }  
75
76         public int Compare(object x, object y) {
77             if (x == y) return 0;
78             if (x == null) return -1;
79             if (y == null) return 1;
80                         
81             String sa = x as String;
82             if (sa != null) {                
83                 String sb = y as String;                
84                 if( sb != null) {
85                     return Compare(sa, sb);                    
86                 }
87             }
88
89             IComparable ia = x as IComparable;
90             if (ia != null) {
91                 return ia.CompareTo(y);
92             }
93
94             throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));            
95         }
96
97        
98         public new bool Equals(Object x, Object y) {
99             if (x == y) return true;
100             if (x == null || y == null) return false;
101             
102             String sa = x as String;
103             if (sa != null) {
104                 String sb = y as String;                
105                 if( sb != null) {
106                     return Equals(sa, sb);
107                 }
108             }
109             return x.Equals(y);                        
110         }
111         
112         public int GetHashCode(object obj) {
113             if( obj == null) {
114                 throw new ArgumentNullException("obj");
115             }
116             Contract.EndContractBlock();
117
118             string s = obj as string;
119             if( s != null) {
120                 return GetHashCode(s);
121             }
122             return obj.GetHashCode();            
123         }
124         
125         public abstract int Compare(String x, String y);
126         public abstract bool Equals(String x, String y);        
127         public abstract int GetHashCode(string obj);        
128     }
129     
130     [Serializable]
131     internal sealed class CultureAwareComparer : StringComparer
132 #if FEATURE_RANDOMIZED_STRING_HASHING
133         , IWellKnownStringEqualityComparer 
134 #endif
135     {
136         private CompareInfo _compareInfo;    
137         private bool            _ignoreCase;
138
139         internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) {
140                _compareInfo = culture.CompareInfo;
141                _ignoreCase = ignoreCase;
142         }
143
144         internal CultureAwareComparer(CompareInfo compareInfo, bool ignoreCase) {
145                _compareInfo = compareInfo;
146                _ignoreCase = ignoreCase;
147         }
148
149         public override int Compare(string x, string y) {
150             if (Object.ReferenceEquals(x, y)) return 0;
151             if (x == null) return -1;
152             if (y == null) return 1;
153             return _compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase :  CompareOptions.None);
154         }
155                 
156         public override bool Equals(string x, string y) {
157             if (Object.ReferenceEquals(x ,y)) return true;
158             if (x == null || y == null) return false;
159
160             return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase :  CompareOptions.None) == 0);
161         }               
162                 
163         public override int GetHashCode(string obj) {
164             if( obj == null) {
165                 throw new ArgumentNullException("obj");
166             }
167             Contract.EndContractBlock();
168
169             CompareOptions options = CompareOptions.None;
170
171             if( _ignoreCase) {
172                 options |= CompareOptions.IgnoreCase;
173             }
174
175             return _compareInfo.GetHashCodeOfString(obj, options);
176         }       
177
178         // Equals method for the comparer itself. 
179         public override bool Equals(Object obj){
180             CultureAwareComparer comparer = obj as CultureAwareComparer;
181             if( comparer == null) {
182                 return false;
183             }
184             return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo));
185         }
186
187         public override int GetHashCode() {
188             int hashCode = _compareInfo.GetHashCode() ;
189             return _ignoreCase ? (~hashCode) : hashCode;
190         }
191
192 #if FEATURE_RANDOMIZED_STRING_HASHING           
193         IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
194             return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
195         }
196
197         IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
198             return this;
199         }
200 #endif
201
202     }
203
204 #if FEATURE_RANDOMIZED_STRING_HASHING
205     internal sealed class CultureAwareRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
206
207         private CompareInfo _compareInfo;    
208         private bool        _ignoreCase;
209         private long        _entropy;
210
211         internal CultureAwareRandomizedComparer(CompareInfo compareInfo, bool ignoreCase) {
212                _compareInfo = compareInfo;
213                _ignoreCase = ignoreCase;
214                _entropy = HashHelpers.GetEntropy();
215         }
216
217         public override int Compare(string x, string y) {
218             if (Object.ReferenceEquals(x, y)) return 0;
219             if (x == null) return -1;
220             if (y == null) return 1;
221             return _compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase :  CompareOptions.None);
222         }
223                 
224         public override bool Equals(string x, string y) {
225             if (Object.ReferenceEquals(x ,y)) return true;
226             if (x == null || y == null) return false;
227
228             return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase :  CompareOptions.None) == 0);
229         }               
230                 
231         public override int GetHashCode(string obj) {
232             if( obj == null) {
233                 throw new ArgumentNullException("obj");
234             }
235             Contract.EndContractBlock();
236
237             CompareOptions options = CompareOptions.None;
238
239             if( _ignoreCase) {
240                 options |= CompareOptions.IgnoreCase;
241             }
242
243             return _compareInfo.GetHashCodeOfString(obj, options, true, _entropy);
244         }       
245
246         // Equals method for the comparer itself. 
247         public override bool Equals(Object obj){
248             CultureAwareRandomizedComparer comparer = obj as CultureAwareRandomizedComparer;
249             if( comparer == null) {
250                 return false;
251             }
252             return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo)) && (this._entropy == comparer._entropy);
253         }
254
255         public override int GetHashCode() {
256             int hashCode = _compareInfo.GetHashCode() ;
257             return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
258         }
259
260         IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
261             return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
262         }
263
264         // We want to serialize the old comparer.
265         IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
266             return new CultureAwareComparer(_compareInfo, _ignoreCase);
267         }
268     }
269 #endif
270
271     // Provide x more optimal implementation of ordinal comparison.
272     [Serializable]
273     internal sealed class OrdinalComparer : StringComparer 
274 #if FEATURE_RANDOMIZED_STRING_HASHING           
275         , IWellKnownStringEqualityComparer
276 #endif
277     {
278         private bool            _ignoreCase;
279        
280         internal OrdinalComparer(bool ignoreCase) {
281                _ignoreCase = ignoreCase;
282         }
283
284         public override int Compare(string x, string y) {
285             if (Object.ReferenceEquals(x, y)) return 0;
286             if (x == null) return -1;
287             if (y == null) return 1;
288             
289             if( _ignoreCase) {
290                 return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
291             }
292                     
293             return String.CompareOrdinal(x, y);                                
294         }
295                 
296         public override bool Equals(string x, string y) {
297             if (Object.ReferenceEquals(x ,y)) return true;
298             if (x == null || y == null) return false;
299
300             if( _ignoreCase) {
301                 if( x.Length != y.Length) {
302                     return false;
303                 }
304                 return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);                                            
305             }
306             return x.Equals(y);
307         }               
308                 
309         public override int GetHashCode(string obj) {
310             if( obj == null) {
311                 throw new ArgumentNullException("obj");
312             }
313             Contract.EndContractBlock();
314
315             if( _ignoreCase) {
316                 return TextInfo.GetHashCodeOrdinalIgnoreCase(obj);
317             }
318                         
319             return obj.GetHashCode();
320         }       
321
322         // Equals method for the comparer itself. 
323         public override bool Equals(Object obj){
324             OrdinalComparer comparer = obj as OrdinalComparer;
325             if( comparer == null) {
326                 return false;
327             }
328             return (this._ignoreCase == comparer._ignoreCase);
329         }
330
331         public override int GetHashCode() {
332             string name = "OrdinalComparer";
333             int hashCode = name.GetHashCode();
334             return _ignoreCase ? (~hashCode) : hashCode;
335         }
336
337 #if FEATURE_RANDOMIZED_STRING_HASHING           
338         IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
339             return new OrdinalRandomizedComparer(_ignoreCase);
340         }
341
342         IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
343             return this;
344         }
345 #endif
346                                 
347     }
348
349
350 #if FEATURE_RANDOMIZED_STRING_HASHING           
351     internal sealed class OrdinalRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
352         private bool            _ignoreCase;
353         private long            _entropy;
354        
355         internal OrdinalRandomizedComparer(bool ignoreCase) {
356                _ignoreCase = ignoreCase;
357                _entropy = HashHelpers.GetEntropy();
358         }
359
360         public override int Compare(string x, string y) {
361             if (Object.ReferenceEquals(x, y)) return 0;
362             if (x == null) return -1;
363             if (y == null) return 1;
364             
365             if( _ignoreCase) {
366                 return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
367             }
368                     
369             return String.CompareOrdinal(x, y);                                
370         }
371                 
372         public override bool Equals(string x, string y) {
373             if (Object.ReferenceEquals(x ,y)) return true;
374             if (x == null || y == null) return false;
375
376             if( _ignoreCase) {
377                 if( x.Length != y.Length) {
378                     return false;
379                 }
380                 return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);                                            
381             }
382             return x.Equals(y);
383         }               
384
385         [System.Security.SecuritySafeCritical]            
386         public override int GetHashCode(string obj) {
387             if( obj == null) {
388                 throw new ArgumentNullException("obj");
389             }
390             Contract.EndContractBlock();
391
392             if( _ignoreCase) {
393                 return TextInfo.GetHashCodeOrdinalIgnoreCase(obj, true, _entropy);
394             }
395                         
396             return String.InternalMarvin32HashString(obj, obj.Length, _entropy);
397         }       
398
399         // Equals method for the comparer itself. 
400         public override bool Equals(Object obj){
401             OrdinalRandomizedComparer comparer = obj as OrdinalRandomizedComparer;
402             if( comparer == null) {
403                 return false;
404             }
405             return (this._ignoreCase == comparer._ignoreCase) && (this._entropy == comparer._entropy);
406         }
407
408         public override int GetHashCode() {
409             string name = "OrdinalRandomizedComparer";
410             int hashCode = name.GetHashCode();
411             return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
412         }
413
414         IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
415             return new OrdinalRandomizedComparer(_ignoreCase);
416         }
417
418         // We want to serialize the old comparer.
419         IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
420             return new OrdinalComparer(_ignoreCase);
421         }                                
422     }
423
424     // This interface is implemented by string comparers in the framework that can opt into
425     // randomized hashing behaviors. 
426     internal interface IWellKnownStringEqualityComparer {
427         // Get an IEqualityComparer that has the same equality comparision rules as "this" but uses Randomized Hashing.
428         IEqualityComparer GetRandomizedEqualityComparer();
429         // Get an IEqaulityComparer that can be serailzied (e.g., it exists in older versions). 
430         IEqualityComparer GetEqualityComparerForSerialization();
431     }
432 #endif
433 }