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