2002-06-27 Martin Baulig <martin@gnome.org>
[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 //
8 // (C) Ximian, Inc.  http://www.ximian.com
9 //
10 // TODO: Mucho left to implement.
11 //
12
13 using System.Collections;
14 using System.Globalization;
15 using System.Runtime.CompilerServices;
16
17 namespace System {
18         internal struct MonoEnumInfo {
19                 internal Type utype;
20                 internal Array values;
21                 internal string[] names;
22                 
23                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
24                 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
25                 
26                 internal static void GetInfo (Type enumType, out MonoEnumInfo info) {
27                         get_enum_info (enumType, out info);
28                         Array.Sort (info.values, info.names);
29                 }
30         };
31
32         [MonoTODO]
33         public abstract class Enum : ValueType, IComparable, IConvertible {
34
35                 // IConvertible methods Start -->
36
37                 [CLSCompliant(false)]
38                 public TypeCode GetTypeCode () {
39                         MonoEnumInfo info;
40                         MonoEnumInfo.GetInfo (this.GetType (), out info);
41                         return Type.GetTypeCode (info.utype);
42                 }
43
44                 bool IConvertible.ToBoolean (IFormatProvider provider)
45                 {
46                         return Convert.ToBoolean (get_value (), provider);
47                 }
48
49                 byte IConvertible.ToByte (IFormatProvider provider)
50                 {
51                         return Convert.ToByte (get_value (), provider);
52                 }
53
54                 char IConvertible.ToChar (IFormatProvider provider)
55                 {
56                         return Convert.ToChar (get_value (), provider);
57                 }
58
59                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
60                 {
61                         return Convert.ToDateTime (get_value (), provider);
62                 }
63
64                 decimal IConvertible.ToDecimal (IFormatProvider provider)
65                 {       
66                         return Convert.ToDecimal (get_value (), provider);
67                 }
68
69                 double IConvertible.ToDouble (IFormatProvider provider)
70                 {       
71                         return Convert.ToDouble (get_value (), provider);
72                 }
73
74                 short IConvertible.ToInt16 (IFormatProvider provider)
75                 {
76                         return Convert.ToInt16 (get_value (), provider);
77                 }
78
79                 int IConvertible.ToInt32 (IFormatProvider provider)
80                 {
81                         return Convert.ToInt32 (get_value (), provider);
82                 }
83
84                 long IConvertible.ToInt64 (IFormatProvider provider)
85                 {
86                         return Convert.ToInt64 (get_value (), provider);
87                 }
88
89                 [CLSCompliant(false)]
90                 sbyte IConvertible.ToSByte (IFormatProvider provider)
91                 {
92                         return Convert.ToSByte (get_value (), provider);
93                 }
94
95                 float IConvertible.ToSingle (IFormatProvider provider)
96                 {
97                         return Convert.ToSingle (get_value (), provider);
98                 }
99
100                 [MonoTODO]
101                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
102                 {
103                         throw new NotImplementedException ();
104                 }
105                 
106                 [CLSCompliant(false)]
107                 public ushort ToUInt16 (IFormatProvider provider)
108                 {
109                         return Convert.ToUInt16 (get_value (), provider);
110                 }
111
112                 [CLSCompliant(false)]
113                 uint IConvertible.ToUInt32 (IFormatProvider provider)
114                 {
115                         return Convert.ToUInt32 (get_value (), provider);
116                 }
117
118                 [CLSCompliant(false)]
119                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
120                 {
121                         return Convert.ToUInt64 (get_value (), provider);
122                 }
123
124                 // <-- End IConvertible methods
125
126                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
127                 private extern object get_value ();
128                 
129                 public static Array GetValues (Type enumType) {
130                         if (null == enumType)
131                                 throw new ArgumentNullException ("enumType cannot be null.");
132
133                         if (!enumType.IsEnum)
134                                 throw new ArgumentException ("enumType is not an Enum type.");
135
136                         MonoEnumInfo info;
137                         MonoEnumInfo.GetInfo (enumType, out info);
138                         return info.values;
139                 }
140                 
141                 public static string[] GetNames (Type enumType) {
142                         if (null == enumType)
143                                 throw new ArgumentNullException ("enumType cannot be null.");
144
145                         if (!enumType.IsEnum)
146                                 throw new ArgumentException ("enumType is not an Enum type.");
147
148                         MonoEnumInfo info;
149                         MonoEnumInfo.GetInfo (enumType, out info);
150                         return info.names;
151                 }
152                 
153                 public static string GetName (Type enumType, object value) {
154                         if (null == enumType)
155                                 throw new ArgumentNullException ("enumType cannot be null.");
156                         if (null == value)
157                                 throw new ArgumentNullException ("value cannot be null.");
158
159                         if (!enumType.IsEnum)
160                                 throw new ArgumentException ("enumType is not an Enum type.");
161
162                         MonoEnumInfo info;
163                         int i;
164                         value = ToObject (enumType, value);
165                         MonoEnumInfo.GetInfo (enumType, out info);
166                         for (i = 0; i < info.values.Length; ++i) {                              
167                                 if (value.Equals (info.values.GetValue (i)))
168                                         return info.names [i];
169                         }
170                         return null;
171                 }
172                 
173                 public static bool IsDefined (Type enumType, object value) {
174                         if (null == enumType)
175                                 throw new ArgumentNullException ("enumType cannot be null.");
176                         if (null == value)
177                                 throw new ArgumentNullException ("value cannot be null.");
178
179                         if (!enumType.IsEnum)
180                                 throw new ArgumentException ("enumType is not an Enum type.");
181
182                         MonoEnumInfo info;
183                         MonoEnumInfo.GetInfo (enumType, out info);
184
185                         Type vType = value.GetType ();
186                         if (vType == typeof(String)) {
187                                 return ((IList)(info.names)).Contains (value);
188                         } else if ((vType == info.utype) || (vType == enumType)) {
189                                 int i;
190                                 value = ToObject (enumType, value);
191                                 MonoEnumInfo.GetInfo (enumType, out info);
192                                 for (i = 0; i < info.values.Length; ++i) {                              
193                                         if (value.Equals (info.values.GetValue (i)))
194                                                 return true;
195                                 }
196                                 return false;
197                         } else {
198                                 throw new ArgumentException("The value parameter is not the correct type."
199                                         + "It must be type String or the same type as the underlying type"
200                                         + "of the Enum.");
201                         }
202                         
203
204                 }
205                 
206                 public static Type GetUnderlyingType (Type enumType) {
207                         if (null == enumType)
208                                 throw new ArgumentNullException ("enumType cannot be null.");
209
210                         if (!enumType.IsEnum)
211                                 throw new ArgumentException ("enumType is not an Enum type.");
212
213                         MonoEnumInfo info;
214                         MonoEnumInfo.GetInfo (enumType, out info);
215                         return info.utype;
216                 }
217
218                 public static object Parse (Type enumType, string value)
219                 {
220                         // Note: Parameters are checked in the other overload
221                         return Parse (enumType, value, false);
222                 }
223
224                 public static object Parse (Type enumType, string value, bool ignoreCase)
225                 {
226                         if (null == enumType)
227                                 throw new ArgumentNullException ("enumType cannot be null.");
228
229                         if (null == value)
230                                 throw new ArgumentNullException ("value cannot be null.");
231
232                         if (!enumType.IsEnum)
233                                 throw new ArgumentException ("enumType is not an Enum type.");
234
235                         if (String.Empty == value.Trim())
236                                 throw new ArgumentException ("value cannot be empty string.");
237
238                         MonoEnumInfo info;
239                         int i;
240                         MonoEnumInfo.GetInfo (enumType, out info);
241
242                         long retVal = 0;
243                         string[] names = value.Split(new char[] {','});
244                         TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
245                         foreach (string name in names) {
246                                 bool found = false;
247                                 for (i = 0; i < info.values.Length; ++i) {                              
248                                         if (String.Compare (name, info.names [i], ignoreCase) == 0) {
249                                                 switch (typeCode) {
250                                                         case TypeCode.Byte:
251                                                                 retVal |= (long)((byte)info.values.GetValue (i));
252                                                                 break;
253                                                         case TypeCode.SByte:
254                                                                 retVal |= (long)((SByte)info.values.GetValue (i));
255                                                                 break;
256                                                         case TypeCode.Int16:
257                                                                 retVal |= (long)((short)info.values.GetValue (i));
258                                                                 break;
259                                                         case TypeCode.Int32:
260                                                                 retVal |= (long)((int)info.values.GetValue (i));
261                                                                 break;
262                                                         case TypeCode.Int64:
263                                                                 retVal |= (long)info.values.GetValue (i);
264                                                                 break;
265                                                         case TypeCode.UInt16:
266                                                                 retVal |= (long)((UInt16)info.values.GetValue (i));
267                                                                 break;
268                                                         case TypeCode.UInt32:
269                                                                 retVal |= (long)((UInt32)info.values.GetValue (i));
270                                                                 break;
271                                                         case TypeCode.UInt64:
272                                                                 retVal |= (long)((UInt64)info.values.GetValue (i));
273                                                                 break;
274                                                 }
275                                                 found = true;
276                                                 break;
277                                         }
278                                 }
279                                 if (!found)
280                                         throw new ArgumentException ("The requested value was not found");
281                                 
282                         }
283                         return ToObject(enumType, retVal);
284                 }
285
286                 /// <summary>
287                 ///   Compares the enum value with another enum value of the same type.
288                 /// </summary>
289                 ///
290                 /// <remarks>
291                 ///   
292                 public int CompareTo (object obj)
293                 {
294                         Type thisType;
295
296                         if (obj == null)
297                                 return 1;
298
299                         thisType = this.GetType();
300                         if (obj.GetType() != thisType){
301                                 throw new ArgumentException(
302                                         "Object must be the same type as the "
303                                         + "enum. The type passed in was " 
304                                         + obj.GetType().ToString()
305                                         + "; the enum type was " 
306                                         + thisType.ToString() + ".");
307                         }
308
309                         object value1, value2;
310
311                         value1 = this.get_value ();
312                         value2 = ((Enum)obj).get_value();
313
314                         return ((IComparable)value1).CompareTo (value2);
315                 }
316                 
317                 public override string ToString ()
318                 {
319                         return ToString ("G", null);
320                 }
321
322                 public string ToString (IFormatProvider provider)
323                 {
324                         return ToString ("G", provider);
325                 }
326
327                 public string ToString (String format)
328                 {
329                         return ToString (format, null);
330                 }
331
332                 [MonoTODO]
333                 public string ToString (String format, IFormatProvider provider)
334                 {
335                         // provider is not used for Enums
336
337                         if (format == String.Empty || format == null){
338                                 format = "G";
339                         }
340                         return Format (this.GetType(), this.get_value (), format);
341                 }
342
343                 public static object ToObject(Type enumType, byte value)
344                 {
345                         return ToObject (enumType, (object)value);
346                 }
347                 
348                 public static object ToObject(Type enumType, short value)
349                 {
350                         return ToObject (enumType, (object)value);
351                 }
352                 public static object ToObject(Type enumType, int value)
353                 {
354                         return ToObject (enumType, (object)value);
355                 }
356                 public static object ToObject(Type enumType, long value)
357                 {
358                         return ToObject (enumType, (object)value);
359                 }
360
361                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
362                 public static extern object ToObject(Type enumType, object value);
363
364                 [CLSCompliant(false)]
365                 public static object ToObject(Type enumType, sbyte value)
366                 {
367                         return ToObject (enumType, (object)value);
368                 }
369                 [CLSCompliant(false)]
370                 public static object ToObject(Type enumType, ushort value)
371                 {
372                         return ToObject (enumType, (object)value);
373                 }
374                 [CLSCompliant(false)]
375                 public static object ToObject(Type enumType, uint value)
376                 {
377                         return ToObject (enumType, (object)value);
378                 }
379                 [CLSCompliant(false)]
380                 public static object ToObject(Type enumType, ulong value)
381                 {
382                         return ToObject (enumType, (object)value);
383                 }
384
385                 public override bool Equals (object obj)
386                 {
387                         if (null == obj)
388                                 return false;
389
390                         if (obj.GetType() != this.GetType())
391                                 return false;
392
393                         object v1 = this.get_value ();
394                         object v2 = ((Enum)obj).get_value ();
395
396                         return v1.Equals (v2);
397                 }
398
399                 public override int GetHashCode ()
400                 {
401                         object v = this.get_value ();
402                         return v.GetHashCode ();
403                 }
404
405                 [MonoTODO]
406                 public static string Format (Type enumType, object value, string format)
407                 {
408                         if (null == enumType)
409                                 throw new ArgumentNullException("enumType cannot be null");
410                         if (null == value)
411                                 throw new ArgumentNullException("value cannot be null");
412                         if (null == format)
413                                 throw new ArgumentNullException("format cannot be null");
414
415                         if (!enumType.IsEnum)
416                                 throw new ArgumentException("enumType is not an Enum Type");
417                         
418                         Type vType = value.GetType();
419                         if (vType != enumType && vType != Enum.GetUnderlyingType(enumType))
420                                 throw new ArgumentException();
421
422
423                         if (format.Length != 1)
424                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
425                                                           "\"x\",\"F\",\"f\",\"D\" or \"d\".");
426
427                         char formatChar = format [0];
428                         if ((formatChar == 'G' || formatChar == 'g') 
429                                 && Attribute.IsDefined(enumType, typeof(FlagsAttribute)))
430                                 formatChar = 'F';
431
432                         string retVal = "";
433                         switch (formatChar) {
434                             case 'G':
435                             case 'g':
436                                 retVal = GetName (enumType, value);
437                                 if (retVal == null)
438                                         retVal = value.ToString();
439                                 break;
440                             case 'X':
441                             case 'x':
442                                 retVal = value.ToString();
443                                 long xValue = Int64.Parse(retVal);
444                                 // FIXME: Not sure if padding should always be with precision
445                                 // 8, if it's culture specific, or what.  This works for me.
446                                 retVal = xValue.ToString("x8");
447                                 break;
448                             case 'D':
449                             case 'd':
450                                 retVal = value.ToString();
451                                 break;
452                             case 'F':
453                             case 'f':
454                                 MonoEnumInfo info;
455                                 MonoEnumInfo.GetInfo (enumType, out info);
456                                 // This is ugly, yes.  We need to handle the different integer
457                                 // types for enums.  If someone else has a better idea, be my guest.
458                                 switch (((Enum)info.values.GetValue (0)).GetTypeCode()) {
459                                         case TypeCode.Byte:
460                                                 byte byteFlag = (byte)value;
461                                                 byte byteenumValue;
462                                                 for (int i = info.values.Length-1; i>=0 && byteFlag != 0; i--) {
463                                                         byteenumValue = (byte)info.values.GetValue (i);
464                                                         if ((byteenumValue & byteFlag) == byteenumValue){
465                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
466                                                                 byteFlag -= byteenumValue;
467                                                         }
468                                                 }
469                                                 break;
470                                         case TypeCode.SByte:
471                                                 SByte sbyteFlag = (SByte)value;
472                                                 SByte sbyteenumValue;
473                                                 for (int i = info.values.Length-1; i>=0 && sbyteFlag != 0; i--) {
474                                                         sbyteenumValue = (SByte)info.values.GetValue (i);
475                                                         if ((sbyteenumValue & sbyteFlag) == sbyteenumValue){
476                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
477                                                                 sbyteFlag -= sbyteenumValue;
478                                                         }
479                                                 }
480                                                 break;
481                                         case TypeCode.Int16:
482                                                 short Int16Flag = (short)value;
483                                                 short Int16enumValue;
484                                                 for (int i = info.values.Length-1; i>=0 && Int16Flag != 0; i--) {
485                                                         Int16enumValue = (short)info.values.GetValue (i);
486                                                         if ((Int16enumValue & Int16Flag) == Int16enumValue){
487                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
488                                                                 Int16Flag -= Int16enumValue;
489                                                         }
490                                                 }
491                                                 break;
492                                         case TypeCode.Int32:
493                                                 int Int32Flag = (int)value;
494                                                 int Int32enumValue;
495                                                 for (int i = info.values.Length-1; i>=0 && Int32Flag != 0; i--) {
496                                                         Int32enumValue = (int)info.values.GetValue (i);
497                                                         if ((Int32enumValue & Int32Flag) == Int32enumValue){
498                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
499                                                                 Int32Flag -= Int32enumValue;
500                                                         }
501                                                 }
502                                                 break;
503                                         case TypeCode.Int64:
504                                                 long Int64Flag = (long)value;
505                                                 long Int64enumValue;
506                                                 for (int i = info.values.Length-1; i>=0 && Int64Flag != 0; i--) {
507                                                         Int64enumValue = (long)info.values.GetValue (i);
508                                                         if ((Int64enumValue & Int64Flag) == Int64enumValue){
509                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
510                                                                 Int64Flag -= Int64enumValue;
511                                                         }
512                                                 }
513                                                 break;
514                                         case TypeCode.UInt16:
515                                                 UInt16 UInt16Flag = (UInt16)value;
516                                                 UInt16 UInt16enumValue;
517                                                 for (int i = info.values.Length-1; i>=0 && UInt16Flag != 0; i--) {
518                                                         UInt16enumValue = (UInt16)info.values.GetValue (i);
519                                                         if ((UInt16enumValue & UInt16Flag) == UInt16enumValue){
520                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
521                                                                 UInt16Flag -= UInt16enumValue;
522                                                         }
523                                                 }
524                                                 break;
525                                         case TypeCode.UInt32:
526                                                 UInt32 UInt32Flag = (UInt32)value;
527                                                 UInt32 UInt32enumValue;
528                                                 for (int i = info.values.Length-1; i>=0 && UInt32Flag != 0; i--) {
529                                                         UInt32enumValue = (UInt32)info.values.GetValue (i);
530                                                         if ((UInt32enumValue & UInt32Flag) == UInt32enumValue){
531                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
532                                                                 UInt32Flag -= UInt32enumValue;
533                                                         }
534                                                 }
535                                                 break;
536                                         case TypeCode.UInt64:
537                                                 UInt64 UInt64Flag = (UInt64)value;
538                                                 UInt64 UInt64enumValue;
539                                                 for (int i = info.values.Length-1; i>=0 && UInt64Flag != 0; i--) {
540                                                         UInt64enumValue = (UInt64)info.values.GetValue (i);
541                                                         if ((UInt64enumValue & UInt64Flag) == UInt64enumValue){
542                                                                 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
543                                                                 UInt64Flag -= UInt64enumValue;
544                                                         }
545                                                 }
546                                                 break;
547                                 }
548                                 break;
549                             default:
550                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
551                                                           "\"x\",\"F\",\"f\",\"D\" or \"d\".");
552                         }
553
554                         return retVal;
555                 }
556         }
557 }