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