Merge pull request #916 from akoeplinger/fix-gac-test
[mono.git] / mcs / class / corlib / System / Enum.cs
1 //
2 // System.Enum.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Nick Drochak (ndrochak@gol.com)
7 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 //
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Globalization;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.InteropServices;
41
42 namespace System
43 {
44         internal struct MonoEnumInfo
45         {
46                 internal Type utype;
47                 internal Array values;
48                 internal string[] names;
49                 internal Dictionary<string, int> name_hash;
50                 [ThreadStatic]
51                 static Dictionary<Type, MonoEnumInfo> cache;
52                 static readonly Dictionary<Type, MonoEnumInfo> global_cache = new Dictionary<Type, MonoEnumInfo> ();
53                 static object global_cache_monitor = new object ();
54                 
55                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
56                 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
57
58                 //
59                 // These comparers are needed because enumerations must be compared
60                 // using unsigned values so that negative numbers can be looked up
61                 // See bug: #371559
62                 //
63                 internal static SByteComparer  sbyte_comparer = new SByteComparer ();
64                 internal static ShortComparer short_comparer = new ShortComparer ();
65                 internal static IntComparer   int_comparer = new IntComparer ();
66                 internal static LongComparer  long_comparer = new LongComparer ();
67                 
68                 internal class SByteComparer : IComparer, System.Collections.Generic.IComparer<sbyte>
69                 {
70                         public int Compare (object x, object y)
71                         {
72                                 sbyte ix = (sbyte) x;
73                                 sbyte iy = (sbyte) y;
74                                 
75                                 return ((byte) ix) - ((byte) iy);
76                         }
77
78                         public int Compare (sbyte ix, sbyte iy)
79                         {
80                                 return ((byte) ix) - ((byte) iy);
81                         }
82                 }
83                 
84                 internal class ShortComparer : IComparer, System.Collections.Generic.IComparer<short>
85                 {
86                         public int Compare (object x, object y)
87                         {
88                                 short ix = (short) x;
89                                 short iy = (short) y;
90                                 
91                                 return ((ushort) ix) - ((ushort) iy);
92                         }
93
94                         public int Compare (short ix, short iy)
95                         {
96                                 return ((ushort) ix) - ((ushort) iy);
97                         }
98                 }
99                 
100                 internal class IntComparer : IComparer, System.Collections.Generic.IComparer<int>
101                   {
102                         public int Compare (object x, object y)
103                         {
104                                 int ix = (int) x;
105                                 int iy = (int) y;
106
107                                 if (ix == iy)
108                                         return 0;
109
110                                 if (((uint) ix) < ((uint) iy))
111                                         return -1;
112                                 return 1;
113                         }
114
115                         public int Compare (int ix, int iy)
116                         {
117                                 if (ix == iy)
118                                         return 0;
119
120                                 if (((uint) ix) < ((uint) iy))
121                                         return -1;
122                                 return 1;
123                         }
124                 }
125
126                 internal class LongComparer : IComparer, System.Collections.Generic.IComparer<long>
127                 {
128                         public int Compare (object x, object y)
129                         {
130                                 long ix = (long) x;
131                                 long iy = (long) y;
132                                 
133                                 if (ix == iy)
134                                         return 0;
135                                 if (((ulong) ix) < ((ulong) iy))
136                                         return -1;
137                                 return 1;
138                         }
139
140                         public int Compare (long ix, long iy)
141                         {
142                                 if (ix == iy)
143                                         return 0;
144                                 if (((ulong) ix) < ((ulong) iy))
145                                         return -1;
146                                 return 1;
147                         }
148                 }
149
150                 private MonoEnumInfo (MonoEnumInfo other)
151                 {
152                         utype = other.utype;
153                         values = other.values;
154                         names = other.names;
155                         name_hash = other.name_hash;
156                 }
157                 
158                 internal static void GetInfo (Type enumType, out MonoEnumInfo info)
159                 {
160                         /* First check the thread-local cache without locking */
161                         if (cache != null && cache.TryGetValue (enumType, out info)) {
162                                 return;
163                         }
164
165                         /* Threads could die, so keep a global cache too */
166                         lock (global_cache_monitor) {
167                                 if (global_cache.TryGetValue (enumType, out info)) {
168                                         if (cache == null)
169                                                 cache = new Dictionary<Type, MonoEnumInfo> ();
170
171                                         cache [enumType] = info;
172                                         return;
173                                 }
174                         }
175
176                         get_enum_info (enumType, out info);
177
178                         Type et = Enum.GetUnderlyingType (enumType);
179                         SortEnums (et, info.values, info.names);
180                         
181                         if (info.names.Length > 50) {
182                                 info.name_hash = new Dictionary<string, int> (info.names.Length);
183                                 for (int i = 0; i <  info.names.Length; ++i)
184                                         info.name_hash [info.names [i]] = i;
185                         }
186                         MonoEnumInfo cached = new MonoEnumInfo (info);
187                         lock (global_cache_monitor) {
188                                 global_cache [enumType] = cached;
189                         }
190                 }
191                 
192                 internal static void SortEnums (Type et, Array values, Array names)
193                 {
194                         IComparer ic = null;
195                         if (et == typeof (int))
196                                 ic = int_comparer;
197                         else if (et == typeof (short))
198                                 ic = short_comparer;
199                         else if (et == typeof (sbyte))
200                                 ic = sbyte_comparer;
201                         else if (et == typeof (long))
202                                 ic = long_comparer;
203                         
204                         Array.Sort (values, names, ic);
205                 }
206         };
207
208         [Serializable]
209         [ComVisible (true)]
210         public abstract class Enum : ValueType, IComparable, IConvertible, IFormattable
211         {
212                 protected Enum ()
213                 {
214                 }
215
216                 // IConvertible methods Start -->
217                 public TypeCode GetTypeCode ()
218                 {
219                         return Type.GetTypeCode (GetUnderlyingType (this.GetType ()));
220                 }
221
222                 bool IConvertible.ToBoolean (IFormatProvider provider)
223                 {
224                         return Convert.ToBoolean (Value, provider);
225                 }
226
227                 byte IConvertible.ToByte (IFormatProvider provider)
228                 {
229                         return Convert.ToByte (Value, provider);
230                 }
231
232                 char IConvertible.ToChar (IFormatProvider provider)
233                 {
234                         return Convert.ToChar (Value, provider);
235                 }
236
237                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
238                 {
239                         return Convert.ToDateTime (Value, provider);
240                 }
241
242                 decimal IConvertible.ToDecimal (IFormatProvider provider)
243                 {       
244                         return Convert.ToDecimal (Value, provider);
245                 }
246
247                 double IConvertible.ToDouble (IFormatProvider provider)
248                 {       
249                         return Convert.ToDouble (Value, provider);
250                 }
251
252                 short IConvertible.ToInt16 (IFormatProvider provider)
253                 {
254                         return Convert.ToInt16 (Value, provider);
255                 }
256
257                 int IConvertible.ToInt32 (IFormatProvider provider)
258                 {
259                         return Convert.ToInt32 (Value, provider);
260                 }
261
262                 long IConvertible.ToInt64 (IFormatProvider provider)
263                 {
264                         return Convert.ToInt64 (Value, provider);
265                 }
266
267                 sbyte IConvertible.ToSByte (IFormatProvider provider)
268                 {
269                         return Convert.ToSByte (Value, provider);
270                 }
271
272                 float IConvertible.ToSingle (IFormatProvider provider)
273                 {
274                         return Convert.ToSingle (Value, provider);
275                 }
276
277                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
278                 {
279                         if (targetType == null)
280                                 throw new ArgumentNullException ("targetType");
281
282                         return Convert.ToType (this, targetType, provider, false);
283                 }
284
285                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
286                 {
287                         return Convert.ToUInt16 (Value, provider);
288                 }
289
290                 uint IConvertible.ToUInt32 (IFormatProvider provider)
291                 {
292                         return Convert.ToUInt32 (Value, provider);
293                 }
294
295                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
296                 {
297                         return Convert.ToUInt64 (Value, provider);
298                 }
299
300                 // <-- End IConvertible methods
301
302                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
303                 private extern object get_value ();
304
305                 // wrap the icall into a property so we don't hav to use the icall everywhere
306                 private object Value {
307                         get { return get_value (); }
308                 }
309
310                 [ComVisible (true)]
311                 public static Array GetValues (Type enumType)
312                 {
313                         if (enumType == null)
314                                 throw new ArgumentNullException ("enumType");
315
316                         if (!enumType.IsEnum)
317                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
318
319                         MonoEnumInfo info;
320                         MonoEnumInfo.GetInfo (enumType, out info);
321                         return (Array) info.values.Clone ();
322                 }
323
324                 [ComVisible (true)]
325                 public static string[] GetNames (Type enumType)
326                 {
327                         if (enumType == null)
328                                 throw new ArgumentNullException ("enumType");
329
330                         if (!enumType.IsEnum)
331                                 throw new ArgumentException ("enumType is not an Enum type.");
332
333                         MonoEnumInfo info;
334                         MonoEnumInfo.GetInfo (enumType, out info);
335                         return (string []) info.names.Clone ();
336                 }
337
338                 //
339                 // The faster, non-boxing version.   It must use the special MonoEnumInfo.xxx_comparers
340                 // to ensure that we are perfoming bitwise compares, and not signed compares.
341                 //
342                 // It also tries to use the non-boxing version of the various Array.BinarySearch methods
343                 //
344                 static int FindPosition (Type enumType, object value, Array values)
345                 {
346                         switch (Type.GetTypeCode (GetUnderlyingType (enumType))) {
347                         case TypeCode.SByte:
348                                 sbyte [] sbyte_array = values as sbyte [];
349                                 return Array.BinarySearch (sbyte_array, (sbyte) value,  MonoEnumInfo.sbyte_comparer);
350
351                         case TypeCode.Byte:
352                                 byte [] byte_array = values as byte [];
353                                 return Array.BinarySearch (byte_array, (byte) value);
354
355                         case TypeCode.Int16:
356                                 short [] short_array = values as short [];
357                                 return Array.BinarySearch (short_array, (short)value, MonoEnumInfo.short_comparer);
358
359                         case TypeCode.UInt16:
360                                 ushort [] ushort_array = values as ushort [];
361                                 return Array.BinarySearch (ushort_array, (ushort)value);
362                 
363                         case TypeCode.Int32:
364                                 int[] int_array = values as int[];
365                                 return Array.BinarySearch (int_array, (int)value, MonoEnumInfo.int_comparer);
366
367                         case TypeCode.UInt32:
368                                 uint[] uint_array = values as uint [];
369                                 return Array.BinarySearch (uint_array, (uint)value);                    
370                         
371                         case TypeCode.Int64:
372                                 long [] long_array = values as long [];
373                                 return Array.BinarySearch (long_array, (long) value,  MonoEnumInfo.long_comparer);
374
375                         case TypeCode.UInt64:
376                                 ulong [] ulong_array = values as ulong [];
377                                 return Array.BinarySearch (ulong_array, (ulong) value);
378
379                         default:
380                                 // This should never happen
381                                 throw new NotSupportedException ("Unknown underlying enum type");
382                         }
383                 }
384         
385                 [ComVisible (true)]
386                 public static string GetName (Type enumType, object value)
387                 {
388                         if (enumType == null)
389                                 throw new ArgumentNullException ("enumType");
390                         if (value == null)
391                                 throw new ArgumentNullException ("value");
392
393                         if (!enumType.IsEnum)
394                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
395
396                         MonoEnumInfo info;
397                         value = ToObject (enumType, value);
398                         MonoEnumInfo.GetInfo (enumType, out info);
399
400                         int i = FindPosition (enumType, value, info.values);
401                         return (i >= 0) ? info.names [i] : null;
402                 }
403
404                 [ComVisible (true)]
405                 public static bool IsDefined (Type enumType, object value)
406                 {
407                         if (enumType == null)
408                                 throw new ArgumentNullException ("enumType");
409                         if (value == null)
410                                 throw new ArgumentNullException ("value");
411
412                         if (!enumType.IsEnum)
413                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
414
415                         MonoEnumInfo info;
416                         MonoEnumInfo.GetInfo (enumType, out info);
417
418                         Type vType = value.GetType ();
419                         if (vType == typeof(String)) {
420                                 return ((IList)(info.names)).Contains (value);
421                         } else if ((vType == info.utype) || (vType == enumType)) {
422                                 value = ToObject (enumType, value);
423                                 MonoEnumInfo.GetInfo (enumType, out info);
424
425                                 return FindPosition (enumType, value, info.values) >= 0;
426                         } else {
427                                 throw new ArgumentException("The value parameter is not the correct type. "
428                                         + "It must be type String or the same type as the underlying type "
429                                         + "of the Enum.");
430                         }
431                 }
432
433                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
434                 private static extern Type get_underlying_type (Type enumType);
435
436                 [ComVisible (true)]
437                 public static Type GetUnderlyingType (Type enumType)
438                 {
439                         if (enumType == null)
440                                 throw new ArgumentNullException ("enumType");
441
442                         if (!enumType.IsEnum)
443                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
444
445                         return get_underlying_type (enumType);
446                 }
447
448                 [ComVisible (true)]
449                 public static object Parse (Type enumType, string value)
450                 {
451                         // Note: Parameters are checked in the other overload
452                         return Parse (enumType, value, false);
453                 }
454
455                 private static int FindName (IDictionary<string, int> name_hash, string [] names, string name,  bool ignoreCase)
456                 {
457                         if (!ignoreCase) {
458                                 /* For enums with many values, use a hash table */
459                                 if (name_hash != null) {
460                                         int val;
461                                         if (name_hash.TryGetValue (name, out val))
462                                                 return val;
463                                 } else {
464                                         for (int i = 0; i < names.Length; ++i) {
465                                                 if (name == names [i])
466                                                         return i;
467                                         }
468                                 }
469                         } else {
470                                 for (int i = 0; i < names.Length; ++i) {
471                                         if (String.Compare (name, names [i], ignoreCase, CultureInfo.InvariantCulture) == 0)
472                                                 return i;
473                                 }
474                         }
475                         return -1;
476                 }
477
478                 // Helper function for dealing with [Flags]-style enums.
479                 private static ulong GetValue (object value, TypeCode typeCode)
480                 {
481                         switch (typeCode) {
482                         case TypeCode.Byte:
483                                 return (byte) value;
484                         case TypeCode.SByte:
485                                 return (byte) ((sbyte) value);
486                         case TypeCode.Int16:
487                                 return (ushort) ((short) value);
488                         case TypeCode.Int32:
489                                 return (uint) ((int) value);
490                         case TypeCode.Int64:
491                                 return (ulong) ((long) value);
492                         case TypeCode.UInt16:
493                                 return (ushort) value;
494                         case TypeCode.UInt32:
495                                 return (uint) value;
496                         case TypeCode.UInt64:
497                                 return (ulong) value;
498                         }
499                         throw new ArgumentException ("typeCode is not a valid type code for an Enum");
500                 }
501
502                 [ComVisible(true)]
503                 public static object Parse (Type enumType, string value, bool ignoreCase)
504                 {
505                         if (enumType == null)
506                                 throw new ArgumentNullException ("enumType");
507
508                         if (value == null)
509                                 throw new ArgumentNullException ("value");
510
511                         if (!enumType.IsEnum)
512                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
513
514                         value = value.Trim ();
515                         if (value.Length == 0)
516                                 throw new ArgumentException ("An empty string is not considered a valid value.");
517
518                         object result;
519                         if (!Parse (enumType, value, ignoreCase, out result))
520                                 throw new ArgumentException (String.Format ("The requested value '{0}' was not found.", value));
521
522                         return result;
523                 }
524
525                 static char [] split_char;
526
527                 static bool Parse<TEnum> (Type enumType, string value, bool ignoreCase, out TEnum result)
528                 {
529                         result = default (TEnum);
530
531                         MonoEnumInfo info;
532                         MonoEnumInfo.GetInfo (enumType, out info);
533
534                         // is 'value' a named constant?
535                         int loc = FindName (info.name_hash, info.names, value, ignoreCase);
536                         if (loc >= 0) {
537                                 result = (TEnum) info.values.GetValue (loc);
538                                 return true;
539                         }
540
541                         TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
542
543                         // is 'value' a list of named constants?
544                         if (value.IndexOf (',') != -1) {
545                                 if (split_char == null)
546                                         split_char = new [] { ',' };
547                                 string [] names = value.Split (split_char);
548                                 ulong retVal = 0;
549                                 for (int i = 0; i < names.Length; ++i) {
550                                         loc = FindName (info.name_hash, info.names, names [i].Trim (), ignoreCase);
551                                         if (loc < 0)
552                                                 return false;
553
554                                         retVal |= GetValue (info.values.GetValue (loc), typeCode);
555                                 }
556                                 result = (TEnum) ToObject (enumType, retVal);
557                                 return true;
558                         }
559
560                         // is 'value' a number?
561                         switch (typeCode) {
562                         case TypeCode.SByte:
563                                 sbyte sb;
564                                 if (!SByte.TryParse (value, out sb))
565                                         return false;
566                                 result = (TEnum) ToObject (enumType, sb);
567                                 break;
568                         case TypeCode.Byte:
569                                 byte b;
570                                 if (!Byte.TryParse (value, out b))
571                                         return false;
572                                 result = (TEnum) ToObject (enumType, b);
573                                 break;
574                         case TypeCode.Int16:
575                                 short i16;
576                                 if (!Int16.TryParse (value, out i16))
577                                         return false;
578                                 result = (TEnum) ToObject (enumType, i16);
579                                 break;
580                         case TypeCode.UInt16:
581                                 ushort u16;
582                                 if (!UInt16.TryParse (value, out u16))
583                                         return false;
584                                 result = (TEnum) ToObject (enumType, u16);
585                                 break;
586                         case TypeCode.Int32:
587                                 int i32;
588                                 if (!Int32.TryParse (value, out i32))
589                                         return false;
590                                 result = (TEnum) ToObject (enumType, i32);
591                                 break;
592                         case TypeCode.UInt32:
593                                 uint u32;
594                                 if (!UInt32.TryParse (value, out u32))
595                                         return false;
596                                 result = (TEnum) ToObject (enumType, u32);
597                                 break;
598                         case TypeCode.Int64:
599                                 long i64;
600                                 if (!Int64.TryParse (value, out i64))
601                                         return false;
602                                 result = (TEnum) ToObject (enumType, i64);
603                                 break;
604                         case TypeCode.UInt64:
605                                 ulong u64;
606                                 if (!UInt64.TryParse (value, out u64))
607                                         return false;
608                                 result = (TEnum) ToObject (enumType, u64);
609                                 break;
610                         default:
611                                 break;
612                         }
613
614                         return true;
615                 }
616
617 #if NET_4_0
618                 public static bool TryParse<TEnum> (string value, out TEnum result) where TEnum : struct
619                 {
620                         return TryParse (value, false, out result);
621                 }
622
623                 public static bool TryParse<TEnum> (string value, bool ignoreCase, out TEnum result) where TEnum : struct
624                 {
625                         Type tenum_type = typeof (TEnum);
626                         if (!tenum_type.IsEnum)
627                                 throw new ArgumentException("TEnum is not an Enum type.", "enumType");
628
629                         result = default (TEnum);
630
631                         if (value == null || value.Trim ().Length == 0)
632                                 return false;
633
634                         return Parse (tenum_type, value, ignoreCase, out result);
635                 }
636 #endif
637
638                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
639                 private extern int compare_value_to (object other);
640
641                 /// <summary>
642                 ///   Compares the enum value with another enum value of the same type.
643                 /// </summary>
644                 ///
645                 /// <remarks/>
646                 public int CompareTo (object target)
647                 {
648                         Type thisType;
649
650                         if (target == null)
651                                 return 1;
652
653                         thisType = this.GetType ();
654                         if (target.GetType() != thisType) {
655                                 throw new ArgumentException (String.Format (
656                                         "Object must be the same type as the enum. The type passed in was {0}; the enum type was {1}.", 
657                                         target.GetType(), thisType));
658                         }
659
660                         return compare_value_to (target);
661                 }
662
663                 public override string ToString ()
664                 {
665                         return Format (GetType (), Value, "G");
666                 }
667
668                 [Obsolete("Provider is ignored, just use ToString")]
669                 public string ToString (IFormatProvider provider)
670                 {
671                         return ToString ("G", provider);
672                 }
673
674                 public string ToString (String format)
675                 {
676                         if (format == String.Empty || format == null)
677                                 format = "G";
678                         
679                         return Format (this.GetType (), this.Value, format);
680                 }
681
682                 [Obsolete("Provider is ignored, just use ToString")]
683                 public string ToString (String format, IFormatProvider provider)
684                 {
685                         // provider is not used for Enums
686
687                         if (format == String.Empty || format == null) {
688                                 format = "G";
689                         }
690                         return Format (this.GetType(), this.Value, format);
691                 }
692
693                 [ComVisible (true)]
694                 public static object ToObject (Type enumType, byte value)
695                 {
696                         return ToObject (enumType, (object)value);
697                 }
698
699                 [ComVisible (true)]
700                 public static object ToObject (Type enumType, short value)
701                 {
702                         return ToObject (enumType, (object)value);
703                 }
704
705                 [ComVisible (true)]
706                 public static object ToObject (Type enumType, int value)
707                 {
708                         return ToObject (enumType, (object)value);
709                 }
710
711                 [ComVisible (true)]
712                 public static object ToObject (Type enumType, long value)
713                 {
714                         return ToObject (enumType, (object)value);
715                 }
716
717                 [ComVisible (true)]
718                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
719                 public static extern object ToObject (Type enumType, object value);
720
721                 [ComVisible (true)]
722                 [CLSCompliant (false)]
723                 public static object ToObject (Type enumType, sbyte value)
724                 {
725                         return ToObject (enumType, (object)value);
726                 }
727
728                 [ComVisible (true)]
729                 [CLSCompliant (false)]
730                 public static object ToObject (Type enumType, ushort value)
731                 {
732                         return ToObject (enumType, (object)value);
733                 }
734
735                 [ComVisible (true)]
736                 [CLSCompliant (false)]
737                 public static object ToObject (Type enumType, uint value)
738                 {
739                         return ToObject (enumType, (object)value);
740                 }
741
742                 [ComVisible (true)]
743                 [CLSCompliant (false)]
744                 public static object ToObject (Type enumType, ulong value)
745                 {
746                         return ToObject (enumType, (object)value);
747                 }
748
749                 public override bool Equals (object obj)
750                 {
751                         return DefaultEquals (this, obj);
752                 }
753
754                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
755                 private extern int get_hashcode ();
756
757                 public override int GetHashCode ()
758                 {
759                         return get_hashcode ();
760                 }
761
762                 private static string FormatSpecifier_X (Type enumType, object value, bool upper)
763                 {
764                         switch (Type.GetTypeCode (enumType)) {
765                         case TypeCode.SByte:
766                                 return NumberFormatter.NumberToString (upper ? "X2" : "x2", ((sbyte)value), null);
767                         case TypeCode.Byte:
768                                 return NumberFormatter.NumberToString (upper ? "X2" : "x2", ((byte)value), null);
769                         case TypeCode.Int16:
770                                 return NumberFormatter.NumberToString (upper ? "X4" : "x4", ((short)value), null);
771                         case TypeCode.UInt16:
772                                 return NumberFormatter.NumberToString (upper ? "X4" : "x4", ((ushort)value), null);
773                         case TypeCode.Int32:
774                                 return NumberFormatter.NumberToString (upper ? "X8" : "x8", ((int)value), null);
775                         case TypeCode.UInt32:
776                                 return NumberFormatter.NumberToString (upper ? "X8" : "x8", ((uint)value), null);
777                         case TypeCode.Int64:
778                                 return NumberFormatter.NumberToString (upper ? "X16" : "x16", ((long)value), null);
779                         case TypeCode.UInt64:
780                                 return NumberFormatter.NumberToString (upper ? "X16" : "x16", ((ulong)value), null);
781                         default:
782                                 throw new Exception ("Invalid type code for enumeration.");
783                         }
784                 }
785
786                 static string FormatFlags (Type enumType, object value)
787                 {
788                         string retVal = String.Empty;
789                         MonoEnumInfo info;
790                         MonoEnumInfo.GetInfo (enumType, out info);
791                         string asString = value.ToString ();
792                         if (asString == "0") {
793                                 retVal = GetName (enumType, value);
794                                 if (retVal == null)
795                                         retVal = asString;
796                                 return retVal;
797                         }
798                         // This is ugly, yes.  We need to handle the different integer
799                         // types for enums.  If someone else has a better idea, be my guest.
800                         switch (((Enum)info.values.GetValue (0)).GetTypeCode ()) {
801                         case TypeCode.SByte: {
802                                 sbyte flags = (sbyte) value;
803                                 sbyte enumValue;
804                                 for (int i = info.values.Length - 1; i >= 0; i--) {
805                                         enumValue = (sbyte) info.values.GetValue (i);
806                                         if (enumValue == 0)
807                                                 continue;
808
809                                         if ((flags & enumValue) == enumValue) {
810                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
811                                                 flags -= enumValue;
812                                         }
813                                 }
814                                 if (flags != 0) return asString;
815                                 }
816                                 break;
817                         case TypeCode.Byte: {
818                                 byte flags = (byte) value;
819                                 byte enumValue;
820                                 for (int i = info.values.Length - 1; i >= 0; i--) {
821                                         enumValue = (byte) info.values.GetValue (i);
822                                         if (enumValue == 0)
823                                                 continue;
824
825                                         if ((flags & enumValue) == enumValue) {
826                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
827                                                 flags -= enumValue;
828                                         }
829                                 }
830                                 if (flags != 0) return asString;
831                                 }
832                                 break;
833                         case TypeCode.Int16: {
834                                 short flags = (short) value;
835                                 short enumValue;
836                                 for (int i = info.values.Length - 1; i >= 0; i--) {
837                                         enumValue = (short) info.values.GetValue (i);
838                                         if (enumValue == 0)
839                                                 continue;
840
841                                         if ((flags & enumValue) == enumValue) {
842                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
843                                                 flags -= enumValue;
844                                         }
845                                 }
846                                 if (flags != 0) return asString;
847                                 }
848                                 break;
849                         case TypeCode.Int32: {
850                                 int flags = (int) value;
851                                 int enumValue;
852                                 for (int i = info.values.Length - 1; i >= 0; i--) {
853                                         enumValue = (int) info.values.GetValue (i);
854                                         if (enumValue == 0)
855                                                 continue;
856
857                                         if ((flags & enumValue) == enumValue) {
858                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
859                                                 flags -= enumValue;
860                                         }
861                                 }
862                                 if (flags != 0) return asString;
863                                 }
864                                 break;
865                         case TypeCode.UInt16: {
866                                 ushort flags = (ushort) value;
867                                 ushort enumValue;
868                                 for (int i = info.values.Length - 1; i >= 0; i--) {
869                                         enumValue = (ushort) info.values.GetValue (i);
870                                         if (enumValue == 0)
871                                                 continue;
872
873                                         if ((flags & enumValue) == enumValue) {
874                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
875                                                 flags -= enumValue;
876                                         }
877                                 }
878                                 if (flags != 0) return asString;
879                                 }
880                                 break;
881                         case TypeCode.UInt32: {
882                                 uint flags = (uint) value;
883                                 uint enumValue;
884                                 for (int i = info.values.Length - 1; i >= 0; i--) {
885                                         enumValue = (uint) info.values.GetValue (i);
886                                         if (enumValue == 0)
887                                                 continue;
888
889                                         if ((flags & enumValue) == enumValue) {
890                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
891                                                 flags -= enumValue;
892                                         }
893                                 }
894                                 if (flags != 0) return asString;
895                                 }
896                                 break;
897                         case TypeCode.Int64: {
898                                 long flags = (long) value;
899                                 long enumValue;
900                                 for (int i = info.values.Length - 1; i >= 0; i--) {
901                                         enumValue = (long) info.values.GetValue (i);
902                                         if (enumValue == 0)
903                                                 continue;
904
905                                         if ((flags & enumValue) == enumValue) {
906                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
907                                                 flags -= enumValue;
908                                         }
909                                 }
910                                 if (flags != 0) return asString;
911                                 }
912                                 break;
913                         case TypeCode.UInt64: {
914                                 ulong flags = (ulong) value;
915                                 ulong enumValue;
916                                 for (int i = info.values.Length - 1; i >= 0; i--) {
917                                         enumValue = (ulong) info.values.GetValue (i);
918                                         if (enumValue == 0)
919                                                 continue;
920
921                                         if ((flags & enumValue) == enumValue) {
922                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
923                                                 flags -= enumValue;
924                                         }
925                                 }
926                                 if (flags != 0) return asString;
927                                 }
928                                 break;
929                         }
930
931                         if (retVal == String.Empty)
932                                 return asString;
933
934                         return retVal;
935                 }
936
937                 [ComVisible (true)]
938                 public static string Format (Type enumType, object value, string format)
939                 {
940                         if (enumType == null)
941                                 throw new ArgumentNullException ("enumType");
942                         if (value == null)
943                                 throw new ArgumentNullException ("value");
944                         if (format == null)
945                                 throw new ArgumentNullException ("format");
946
947                         if (!enumType.IsEnum)
948                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
949                         
950                         Type vType = value.GetType();
951                         Type underlyingType = Enum.GetUnderlyingType (enumType);
952                         if (vType.IsEnum) {
953                                 if (vType != enumType)
954                                         throw new ArgumentException (string.Format(
955                                                 "Object must be the same type as the enum. The type" +
956                                                 " passed in was {0}; the enum type was {1}.",
957                                                 vType.FullName, enumType.FullName));
958                         } else if (vType != underlyingType) {
959                                 throw new ArgumentException (string.Format (
960                                         "Enum underlying type and the object must be the same type" +
961                                         " or object. Type passed in was {0}; the enum underlying" +
962                                         " type was {1}.", vType.FullName, underlyingType.FullName));
963                         }
964
965                         if (format.Length == 1) {
966                                 switch (format [0]) {
967                                 case 'f':
968                                 case 'F':
969                                         return FormatFlags (enumType, value);
970                                 case 'g':
971                                 case 'G':
972                                         if (!enumType.IsDefined (typeof(FlagsAttribute), false))
973                                                 return GetName (enumType, value) ?? value.ToString ();
974                                         
975                                         goto case 'f';
976                                 case 'X':
977                                         return FormatSpecifier_X (enumType, value, true);
978                                 case 'x':
979                                         return FormatSpecifier_X (enumType, value, false);
980                                 case 'D':
981                                 case 'd':
982                                         if (vType.IsEnum)
983                                                 value = ((Enum) value).Value;
984
985                                         return value.ToString ();
986                                 }
987                         }               
988
989                         throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
990                                         "\"x\",\"F\",\"f\",\"D\" or \"d\".");
991                 }
992
993 #if NET_4_0
994                 public bool HasFlag (Enum flag)
995                 {
996                         var val = get_value ();
997                         ulong mvalue = GetValue (val, Type.GetTypeCode (val.GetType ()));
998                         ulong fvalue = GetValue (flag, Type.GetTypeCode (flag.GetType ()));
999
1000                         return ((mvalue & fvalue) == fvalue);
1001                 }
1002 #endif
1003         }
1004 }