2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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 // TODO: Mucho left to implement.
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System.Collections;
38 using System.Globalization;
39 using System.Runtime.CompilerServices;
40
41 namespace System
42 {
43         internal struct MonoEnumInfo
44         {
45                 internal Type utype;
46                 internal Array values;
47                 internal string[] names;
48                 static Hashtable cache;
49                 
50                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
51                 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
52                 
53                 private MonoEnumInfo (MonoEnumInfo other)
54                 {
55                         utype = other.utype;
56                         values = other.values;
57                         names = other.names;
58                 }
59
60                 internal static void GetInfo (Type enumType, out MonoEnumInfo info)
61                 {
62                         if (cache == null)
63                                 cache = Hashtable.Synchronized (new Hashtable ());
64                         lock (cache) {
65                                 if (cache.ContainsKey (enumType)) {
66                                         info = (MonoEnumInfo) cache [enumType];
67                                         return;
68                                 }
69                                 get_enum_info (enumType, out info);
70                                 Array.Sort (info.values, info.names);
71                                 cache.Add (enumType, new MonoEnumInfo (info));
72                         }
73                 }
74         };
75
76         [Serializable]
77         public abstract class Enum : ValueType, IComparable, IConvertible, IFormattable
78         {
79                 protected Enum ()
80                 {
81                 }
82
83                 // IConvertible methods Start -->
84                 public TypeCode GetTypeCode ()
85                 {
86                         MonoEnumInfo info;
87                         MonoEnumInfo.GetInfo (this.GetType (), out info);
88                         return Type.GetTypeCode (info.utype);
89                 }
90
91                 bool IConvertible.ToBoolean (IFormatProvider provider)
92                 {
93                         return Convert.ToBoolean (get_value (), provider);
94                 }
95
96                 byte IConvertible.ToByte (IFormatProvider provider)
97                 {
98                         return Convert.ToByte (get_value (), provider);
99                 }
100
101                 char IConvertible.ToChar (IFormatProvider provider)
102                 {
103                         return Convert.ToChar (get_value (), provider);
104                 }
105
106                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
107                 {
108                         return Convert.ToDateTime (get_value (), provider);
109                 }
110
111                 decimal IConvertible.ToDecimal (IFormatProvider provider)
112                 {       
113                         return Convert.ToDecimal (get_value (), provider);
114                 }
115
116                 double IConvertible.ToDouble (IFormatProvider provider)
117                 {       
118                         return Convert.ToDouble (get_value (), provider);
119                 }
120
121                 short IConvertible.ToInt16 (IFormatProvider provider)
122                 {
123                         return Convert.ToInt16 (get_value (), provider);
124                 }
125
126                 int IConvertible.ToInt32 (IFormatProvider provider)
127                 {
128                         return Convert.ToInt32 (get_value (), provider);
129                 }
130
131                 long IConvertible.ToInt64 (IFormatProvider provider)
132                 {
133                         return Convert.ToInt64 (get_value (), provider);
134                 }
135
136                 sbyte IConvertible.ToSByte (IFormatProvider provider)
137                 {
138                         return Convert.ToSByte (get_value (), provider);
139                 }
140
141                 float IConvertible.ToSingle (IFormatProvider provider)
142                 {
143                         return Convert.ToSingle (get_value (), provider);
144                 }
145
146                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
147                 {
148                         return Convert.ToType (get_value (), conversionType, provider);
149                 }
150
151                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
152                 {
153                         return Convert.ToUInt16 (get_value (), provider);
154                 }
155
156                 uint IConvertible.ToUInt32 (IFormatProvider provider)
157                 {
158                         return Convert.ToUInt32 (get_value (), provider);
159                 }
160
161                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
162                 {
163                         return Convert.ToUInt64 (get_value (), provider);
164                 }
165                 // <-- End IConvertible methods
166
167                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
168                 private extern object get_value ();
169
170                 public static Array GetValues (Type enumType)
171                 {
172                         if (enumType == null)
173                                 throw new ArgumentNullException ("enumType");
174
175                         if (!enumType.IsEnum)
176                                 throw new ArgumentException ("enumType is not an Enum type.");
177
178                         MonoEnumInfo info;
179                         MonoEnumInfo.GetInfo (enumType, out info);
180                         return (Array) info.values.Clone ();
181                 }
182
183                 public static string[] GetNames (Type enumType)
184                 {
185                         if (enumType == null)
186                                 throw new ArgumentNullException ("enumType");
187
188                         if (!enumType.IsEnum)
189                                 throw new ArgumentException ("enumType is not an Enum type.");
190
191                         MonoEnumInfo info;
192                         MonoEnumInfo.GetInfo (enumType, out info);
193                         return (string []) info.names.Clone ();
194                 }
195
196                 public static string GetName (Type enumType, object value)
197                 {
198                         if (enumType == null)
199                                 throw new ArgumentNullException ("enumType");
200                         if (value == null)
201                                 throw new ArgumentNullException ("value");
202
203                         if (!enumType.IsEnum)
204                                 throw new ArgumentException ("enumType is not an Enum type.");
205
206                         MonoEnumInfo info;
207                         int i;
208                         value = ToObject (enumType, value);
209                         MonoEnumInfo.GetInfo (enumType, out info);
210                         for (i = 0; i < info.values.Length; ++i) {
211                                 if (value.Equals (info.values.GetValue (i)))
212                                         return info.names [i];
213                         }
214                         return null;
215                 }
216
217                 public static bool IsDefined (Type enumType, object value)
218                 {
219                         if (enumType == null)
220                                 throw new ArgumentNullException ("enumType");
221                         if (value == null)
222                                 throw new ArgumentNullException ("value");
223
224                         if (!enumType.IsEnum)
225                                 throw new ArgumentException ("enumType is not an Enum type.");
226
227                         MonoEnumInfo info;
228                         MonoEnumInfo.GetInfo (enumType, out info);
229
230                         Type vType = value.GetType ();
231                         if (vType == typeof(String)) {
232                                 return ((IList)(info.names)).Contains (value);
233                         } else if ((vType == info.utype) || (vType == enumType)) {
234                                 int i;
235                                 value = ToObject (enumType, value);
236                                 MonoEnumInfo.GetInfo (enumType, out info);
237                                 for (i = 0; i < info.values.Length; ++i) {
238                                         if (value.Equals (info.values.GetValue (i)))
239                                                 return true;
240                                 }
241                                 return false;
242                         } else {
243                                 throw new ArgumentException("The value parameter is not the correct type."
244                                         + "It must be type String or the same type as the underlying type"
245                                         + "of the Enum.");
246                         }
247                 }
248
249                 public static Type GetUnderlyingType (Type enumType)
250                 {
251                         if (enumType == null)
252                                 throw new ArgumentNullException ("enumType");
253
254                         if (!enumType.IsEnum)
255                                 throw new ArgumentException ("enumType is not an Enum type.");
256
257                         MonoEnumInfo info;
258                         MonoEnumInfo.GetInfo (enumType, out info);
259                         return info.utype;
260                 }
261
262                 public static object Parse (Type enumType, string value)
263                 {
264                         // Note: Parameters are checked in the other overload
265                         return Parse (enumType, value, false);
266                 }
267
268                 public static object Parse (Type enumType, string value, bool ignoreCase)
269                 {
270                         if (enumType == null)
271                                 throw new ArgumentNullException ("enumType");
272
273                         if (value == null)
274                                 throw new ArgumentNullException ("value");
275
276                         if (!enumType.IsEnum)
277                                 throw new ArgumentException ("enumType is not an Enum type.");
278
279                         if (String.Empty == value.Trim())
280                                 throw new ArgumentException ("value cannot be empty string.");
281
282                         MonoEnumInfo info;
283                         int i;
284                         MonoEnumInfo.GetInfo (enumType, out info);
285
286                         long retVal = 0;
287
288                         TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
289
290                         try {
291                                 // Attempt to convert to numeric type
292                                 return ToObject (enumType, Convert.ChangeType (value, typeCode) );
293                         } catch {}
294
295                         string[] names = value.Split (new char[] {','});
296                         for (i = 0; i < names.Length; ++i)
297                                 names [i] = names [i].Trim ();
298
299                         foreach (string name in names) {
300                                 bool found = false;
301                                 for (i = 0; i < info.values.Length; ++i) {
302                                         if (String.Compare (name, info.names [i], ignoreCase, CultureInfo.InvariantCulture) == 0) {
303                                                 switch (typeCode) {
304                                                         case TypeCode.Byte:
305                                                                 retVal |= (long)((byte)info.values.GetValue (i));
306                                                                 break;
307                                                         case TypeCode.SByte:
308                                                                 // use the unsigned version in the cast to avoid 
309                                                                 // compiler warning
310                                                                 retVal |= (long)((byte)(SByte)info.values.GetValue (i));
311                                                                 break;
312                                                         case TypeCode.Int16:
313                                                                 // use the unsigned version in the cast to avoid 
314                                                                 // compiler warning
315                                                                 retVal |= (long)((ushort)(short)info.values.GetValue (i));
316                                                                 break;
317                                                         case TypeCode.Int32:
318                                                                 // use the unsigned version in the cast to avoid 
319                                                                 // compiler warning
320                                                                 retVal |= (long)((uint)(int)info.values.GetValue (i));
321                                                                 break;
322                                                         case TypeCode.Int64:
323                                                                 retVal |= (long)info.values.GetValue (i);
324                                                                 break;
325                                                         case TypeCode.UInt16:
326                                                                 retVal |= (long)((UInt16)info.values.GetValue (i));
327                                                                 break;
328                                                         case TypeCode.UInt32:
329                                                                 retVal |= (long)((UInt32)info.values.GetValue (i));
330                                                                 break;
331                                                         case TypeCode.UInt64:
332                                                                 retVal |= (long)((UInt64)info.values.GetValue (i));
333                                                                 break;
334                                                 }
335                                                 found = true;
336                                                 break;
337                                         }
338                                 }
339                                 if (!found)
340                                         throw new ArgumentException ("The requested value was not found.");
341                                 
342                         }
343                         return ToObject (enumType, retVal);
344                 }
345
346                 /// <summary>
347                 ///   Compares the enum value with another enum value of the same type.
348                 /// </summary>
349                 ///
350                 /// <remarks/>
351                 public int CompareTo (object obj)
352                 {
353                         Type thisType;
354
355                         if (obj == null)
356                                 return 1;
357
358                         thisType = this.GetType ();
359                         if (obj.GetType() != thisType) {
360                                 throw new ArgumentException (
361                                         "Object must be the same type as the "
362                                         + "enum. The type passed in was " 
363                                         + obj.GetType().ToString ()
364                                         + "; the enum type was " 
365                                         + thisType.ToString () + ".");
366                         }
367
368                         object value1, value2;
369
370                         value1 = this.get_value ();
371                         value2 = ((Enum)obj).get_value ();
372
373                         return ((IComparable)value1).CompareTo (value2);
374                 }
375
376                 public override string ToString ()
377                 {
378                         return ToString ("G", null);
379                 }
380
381                 public string ToString (IFormatProvider provider)
382                 {
383                         return ToString ("G", provider);
384                 }
385
386                 public string ToString (String format)
387                 {
388                         return ToString (format, null);
389                 }
390
391                 public string ToString (String format, IFormatProvider provider)
392                 {
393                         // provider is not used for Enums
394
395                         if (format == String.Empty || format == null) {
396                                 format = "G";
397                         }
398                         return Format (this.GetType(), this.get_value (), format);
399                 }
400
401                 public static object ToObject (Type enumType, byte value)
402                 {
403                         return ToObject (enumType, (object)value);
404                 }
405
406                 public static object ToObject (Type enumType, short value)
407                 {
408                         return ToObject (enumType, (object)value);
409                 }
410
411                 public static object ToObject (Type enumType, int value)
412                 {
413                         return ToObject (enumType, (object)value);
414                 }
415
416                 public static object ToObject (Type enumType, long value)
417                 {
418                         return ToObject (enumType, (object)value);
419                 }
420
421                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
422                 public static extern object ToObject (Type enumType, object value);
423
424                 [CLSCompliant (false)]
425                 public static object ToObject (Type enumType, sbyte value)
426                 {
427                         return ToObject (enumType, (object)value);
428                 }
429
430                 [CLSCompliant (false)]
431                 public static object ToObject (Type enumType, ushort value)
432                 {
433                         return ToObject (enumType, (object)value);
434                 }
435
436                 [CLSCompliant (false)]
437                 public static object ToObject (Type enumType, uint value)
438                 {
439                         return ToObject (enumType, (object)value);
440                 }
441
442                 [CLSCompliant (false)]
443                 public static object ToObject (Type enumType, ulong value)
444                 {
445                         return ToObject (enumType, (object)value);
446                 }
447
448                 public override bool Equals (object obj)
449                 {
450                         if (obj == null || !(obj is Enum))
451                                 return false;
452
453                         if (obj.GetType() != this.GetType ())
454                                 return false;
455
456                         object v1 = this.get_value ();
457                         object v2 = ((Enum)obj).get_value ();
458
459                         return v1.Equals (v2);
460                 }
461
462                 public override int GetHashCode ()
463                 {
464                         object v = this.get_value ();
465                         return v.GetHashCode ();
466                 }
467
468                 private static string FormatSpecifier_X (Type enumType, object value)
469                 {
470                         // FIXME: Not sure if padding should always be with precision
471                         // 8, if it's culture specific, or what.  This works for me.
472                         const string format = "x8";
473
474                         switch (Type.GetTypeCode (enumType)) {
475                                 case TypeCode.Char:
476                                         // Char doesn't support ToString(format), so convert to an int and
477                                         // use that...
478                                         char v = (char) value;
479                                         return Convert.ToInt32 (v).ToString(format);
480                                 case TypeCode.SByte:
481                                         return ((sbyte)value).ToString (format);
482                                 case TypeCode.Byte:
483                                         return ((byte)value).ToString (format);
484                                 case TypeCode.Int16:
485                                         return ((short)value).ToString (format);
486                                 case TypeCode.UInt16:
487                                         return ((ushort)value).ToString (format);
488                                 case TypeCode.Int32:
489                                         return ((int)value).ToString (format);
490                                 case TypeCode.UInt32:
491                                         return ((uint)value).ToString (format);
492                                 case TypeCode.Int64:
493                                         return ((long)value).ToString (format);
494                                 case TypeCode.UInt64:
495                                         return ((ulong)value).ToString (format);
496                                 default:
497                                         throw new Exception ("Invalid type code for enumeration.");
498                         }
499                 }
500
501                 static string FormatFlags (Type enumType, object value)
502                 {
503                         string retVal = "";
504                         MonoEnumInfo info;
505                         MonoEnumInfo.GetInfo (enumType, out info);
506                         string asString = value.ToString ();
507                         if (asString == "0") {
508                                 retVal = GetName (enumType, value);
509                                 if (retVal == null)
510                                         retVal = asString;
511                                 return retVal;
512                         }
513                         // This is ugly, yes.  We need to handle the different integer
514                         // types for enums.  If someone else has a better idea, be my guest.
515                         switch (((Enum)info.values.GetValue (0)).GetTypeCode ()) {
516                         case TypeCode.SByte: {
517                                 sbyte flags = (sbyte) value;
518                                 sbyte enumValue;
519                                 for (int i = info.values.Length - 1; i >= 0; i--) {
520                                         enumValue = (sbyte) info.values.GetValue (i);
521                                         if (i == 0 && enumValue == 0)
522                                                 continue;
523
524                                         if ((flags & enumValue) == enumValue) {
525                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
526                                                 flags -= enumValue;
527                                         }
528                                 }
529                                 if (flags != 0) return asString;
530                                 }
531                                 break;
532                         case TypeCode.Byte: {
533                                 byte flags = (byte) value;
534                                 byte enumValue;
535                                 for (int i = info.values.Length - 1; i >= 0; i--) {
536                                         enumValue = (byte) info.values.GetValue (i);
537                                         if (i == 0 && enumValue == 0)
538                                                 continue;
539
540                                         if ((flags & enumValue) == enumValue) {
541                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
542                                                 flags -= enumValue;
543                                         }
544                                 }
545                                 if (flags != 0) return asString;
546                                 }
547                                 break;
548                         case TypeCode.Int16: {
549                                 short flags = (short) value;
550                                 short enumValue;
551                                 for (int i = info.values.Length - 1; i >= 0; i--) {
552                                         enumValue = (short) info.values.GetValue (i);
553                                         if (i == 0 && enumValue == 0)
554                                                 continue;
555
556                                         if ((flags & enumValue) == enumValue) {
557                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
558                                                 flags -= enumValue;
559                                         }
560                                 }
561                                 if (flags != 0) return asString;
562                                 }
563                                 break;
564                         case TypeCode.Int32: {
565                                 int flags = (int) value;
566                                 int enumValue;
567                                 for (int i = info.values.Length - 1; i >= 0; i--) {
568                                         enumValue = (int) info.values.GetValue (i);
569                                         if (i == 0 && enumValue == 0)
570                                                 continue;
571
572                                         if ((flags & enumValue) == enumValue) {
573                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
574                                                 flags -= enumValue;
575                                         }
576                                 }
577                                 if (flags != 0) return asString;
578                                 }
579                                 break;
580                         case TypeCode.UInt16: {
581                                 ushort flags = (ushort) value;
582                                 ushort enumValue;
583                                 for (int i = info.values.Length - 1; i >= 0; i--) {
584                                         enumValue = (ushort) info.values.GetValue (i);
585                                         if (i == 0 && enumValue == 0)
586                                                 continue;
587
588                                         if ((flags & enumValue) == enumValue) {
589                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
590                                                 flags -= enumValue;
591                                         }
592                                 }
593                                 if (flags != 0) return asString;
594                                 }
595                                 break;
596                         case TypeCode.UInt32: {
597                                 uint flags = (uint) value;
598                                 uint enumValue;
599                                 for (int i = info.values.Length - 1; i >= 0; i--) {
600                                         enumValue = (uint) info.values.GetValue (i);
601                                         if (i == 0 && enumValue == 0)
602                                                 continue;
603
604                                         if ((flags & enumValue) == enumValue) {
605                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
606                                                 flags -= enumValue;
607                                         }
608                                 }
609                                 if (flags != 0) return asString;
610                                 }
611                                 break;
612                         case TypeCode.Int64: {
613                                 long flags = (long) value;
614                                 long enumValue;
615                                 for (int i = info.values.Length - 1; i >= 0; i--) {
616                                         enumValue = (long) info.values.GetValue (i);
617                                         if (i == 0 && enumValue == 0)
618                                                 continue;
619
620                                         if ((flags & enumValue) == enumValue) {
621                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
622                                                 flags -= enumValue;
623                                         }
624                                 }
625                                 if (flags != 0) return asString;
626                                 }
627                                 break;
628                         case TypeCode.UInt64: {
629                                 ulong flags = (ulong) value;
630                                 ulong enumValue;
631                                 for (int i = info.values.Length - 1; i >= 0; i--) {
632                                         enumValue = (ulong) info.values.GetValue (i);
633                                         if (i == 0 && enumValue == 0)
634                                                 continue;
635
636                                         if ((flags & enumValue) == enumValue) {
637                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
638                                                 flags -= enumValue;
639                                         }
640                                 }
641                                 if (flags != 0) return asString;
642                                 }
643                                 break;
644                         }
645
646                         if (retVal == "")
647                                 return asString;
648
649                         return retVal;
650                 }
651
652                 public static string Format (Type enumType, object value, string format)
653                 {
654                         if (enumType == null)
655                                 throw new ArgumentNullException ("enumType");
656                         if (value == null)
657                                 throw new ArgumentNullException ("value");
658                         if (format == null)
659                                 throw new ArgumentNullException ("format");
660
661                         if (!enumType.IsEnum)
662                                 throw new ArgumentException ("enumType is not an Enum Type.");
663                         
664                         Type vType = value.GetType();
665                         if (vType != enumType && vType != Enum.GetUnderlyingType (enumType))
666                                 throw new ArgumentException ();
667
668                         if (format.Length != 1)
669                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
670                                         "\"x\",\"F\",\"f\",\"D\" or \"d\".");
671
672                         char formatChar = format [0];
673                         string retVal;
674                         if ((formatChar == 'G' || formatChar == 'g')) {
675                                 if (!Attribute.IsDefined (enumType, typeof(FlagsAttribute))) {
676                                         retVal = GetName (enumType, value);
677                                         if (retVal == null)
678                                                 retVal = value.ToString();
679
680                                         return retVal;
681                                 }
682
683                                 formatChar = 'f';
684                         }
685                         
686                         if ((formatChar == 'f' || formatChar == 'F'))
687                                 return FormatFlags (enumType, value);
688
689                         retVal = "";
690                         switch (formatChar) {
691                         case 'X':
692                         case 'x':
693                                 retVal = FormatSpecifier_X (enumType, value);
694                                 break;
695                         case 'D':
696                         case 'd':
697                                 if (Enum.GetUnderlyingType (enumType) == typeof (ulong)) {
698                                         ulong ulongValue = Convert.ToUInt64 (value);
699                                         retVal = ulongValue.ToString ();
700                                 } else {
701                                         long longValue = Convert.ToInt64 (value);
702                                         retVal = longValue.ToString ();
703                                 }
704                                 break;
705                         default:
706                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
707                                         "\"x\",\"F\",\"f\",\"D\" or \"d\".");
708                         }
709                         return retVal;
710                 }
711         }
712 }