5 // Miguel de Icaza (miguel@ximian.com)
6 // Nick Drochak (ndrochak@gol.com)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 // (C) Ximian, Inc. http://www.ximian.com
11 // TODO: Mucho left to implement.
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
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.
37 using System.Collections;
38 using System.Globalization;
39 using System.Runtime.CompilerServices;
43 internal struct MonoEnumInfo
46 internal Array values;
47 internal string[] names;
48 static Hashtable cache;
50 [MethodImplAttribute (MethodImplOptions.InternalCall)]
51 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
53 private MonoEnumInfo (MonoEnumInfo other)
56 values = other.values;
60 internal static void GetInfo (Type enumType, out MonoEnumInfo info)
63 cache = Hashtable.Synchronized (new Hashtable ());
65 if (cache.ContainsKey (enumType)) {
66 info = (MonoEnumInfo) cache [enumType];
69 get_enum_info (enumType, out info);
70 Array.Sort (info.values, info.names);
71 cache.Add (enumType, new MonoEnumInfo (info));
77 public abstract class Enum : ValueType, IComparable, IConvertible, IFormattable
83 // IConvertible methods Start -->
84 public TypeCode GetTypeCode ()
87 MonoEnumInfo.GetInfo (this.GetType (), out info);
88 return Type.GetTypeCode (info.utype);
91 bool IConvertible.ToBoolean (IFormatProvider provider)
93 return Convert.ToBoolean (get_value (), provider);
96 byte IConvertible.ToByte (IFormatProvider provider)
98 return Convert.ToByte (get_value (), provider);
101 char IConvertible.ToChar (IFormatProvider provider)
103 return Convert.ToChar (get_value (), provider);
106 DateTime IConvertible.ToDateTime (IFormatProvider provider)
108 return Convert.ToDateTime (get_value (), provider);
111 decimal IConvertible.ToDecimal (IFormatProvider provider)
113 return Convert.ToDecimal (get_value (), provider);
116 double IConvertible.ToDouble (IFormatProvider provider)
118 return Convert.ToDouble (get_value (), provider);
121 short IConvertible.ToInt16 (IFormatProvider provider)
123 return Convert.ToInt16 (get_value (), provider);
126 int IConvertible.ToInt32 (IFormatProvider provider)
128 return Convert.ToInt32 (get_value (), provider);
131 long IConvertible.ToInt64 (IFormatProvider provider)
133 return Convert.ToInt64 (get_value (), provider);
136 sbyte IConvertible.ToSByte (IFormatProvider provider)
138 return Convert.ToSByte (get_value (), provider);
141 float IConvertible.ToSingle (IFormatProvider provider)
143 return Convert.ToSingle (get_value (), provider);
146 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
148 return Convert.ToType (get_value (), conversionType, provider);
151 ushort IConvertible.ToUInt16 (IFormatProvider provider)
153 return Convert.ToUInt16 (get_value (), provider);
156 uint IConvertible.ToUInt32 (IFormatProvider provider)
158 return Convert.ToUInt32 (get_value (), provider);
161 ulong IConvertible.ToUInt64 (IFormatProvider provider)
163 return Convert.ToUInt64 (get_value (), provider);
165 // <-- End IConvertible methods
167 [MethodImplAttribute (MethodImplOptions.InternalCall)]
168 private extern object get_value ();
170 public static Array GetValues (Type enumType)
172 if (enumType == null)
173 throw new ArgumentNullException ("enumType");
175 if (!enumType.IsEnum)
176 throw new ArgumentException ("enumType is not an Enum type.");
179 MonoEnumInfo.GetInfo (enumType, out info);
180 return (Array) info.values.Clone ();
183 public static string[] GetNames (Type enumType)
185 if (enumType == null)
186 throw new ArgumentNullException ("enumType");
188 if (!enumType.IsEnum)
189 throw new ArgumentException ("enumType is not an Enum type.");
192 MonoEnumInfo.GetInfo (enumType, out info);
193 return (string []) info.names.Clone ();
196 public static string GetName (Type enumType, object value)
198 if (enumType == null)
199 throw new ArgumentNullException ("enumType");
201 throw new ArgumentNullException ("value");
203 if (!enumType.IsEnum)
204 throw new ArgumentException ("enumType is not an Enum type.");
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];
217 public static bool IsDefined (Type enumType, object value)
219 if (enumType == null)
220 throw new ArgumentNullException ("enumType");
222 throw new ArgumentNullException ("value");
224 if (!enumType.IsEnum)
225 throw new ArgumentException ("enumType is not an Enum type.");
228 MonoEnumInfo.GetInfo (enumType, out info);
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)) {
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)))
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"
249 public static Type GetUnderlyingType (Type enumType)
251 if (enumType == null)
252 throw new ArgumentNullException ("enumType");
254 if (!enumType.IsEnum)
255 throw new ArgumentException ("enumType is not an Enum type.");
258 MonoEnumInfo.GetInfo (enumType, out info);
262 public static object Parse (Type enumType, string value)
264 // Note: Parameters are checked in the other overload
265 return Parse (enumType, value, false);
268 public static object Parse (Type enumType, string value, bool ignoreCase)
270 if (enumType == null)
271 throw new ArgumentNullException ("enumType");
274 throw new ArgumentNullException ("value");
276 if (!enumType.IsEnum)
277 throw new ArgumentException ("enumType is not an Enum type.");
279 if (String.Empty == value.Trim())
280 throw new ArgumentException ("value cannot be empty string.");
284 MonoEnumInfo.GetInfo (enumType, out info);
288 TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
291 // Attempt to convert to numeric type
292 return ToObject (enumType, Convert.ChangeType (value, typeCode) );
295 string[] names = value.Split (new char[] {','});
296 for (i = 0; i < names.Length; ++i)
297 names [i] = names [i].Trim ();
299 foreach (string name in names) {
301 for (i = 0; i < info.values.Length; ++i) {
302 if (String.Compare (name, info.names [i], ignoreCase, CultureInfo.InvariantCulture) == 0) {
305 retVal |= (long)((byte)info.values.GetValue (i));
308 // use the unsigned version in the cast to avoid
310 retVal |= (long)((byte)(SByte)info.values.GetValue (i));
313 // use the unsigned version in the cast to avoid
315 retVal |= (long)((ushort)(short)info.values.GetValue (i));
318 // use the unsigned version in the cast to avoid
320 retVal |= (long)((uint)(int)info.values.GetValue (i));
323 retVal |= (long)info.values.GetValue (i);
325 case TypeCode.UInt16:
326 retVal |= (long)((UInt16)info.values.GetValue (i));
328 case TypeCode.UInt32:
329 retVal |= (long)((UInt32)info.values.GetValue (i));
331 case TypeCode.UInt64:
332 retVal |= (long)((UInt64)info.values.GetValue (i));
340 throw new ArgumentException ("The requested value was not found.");
343 return ToObject (enumType, retVal);
347 /// Compares the enum value with another enum value of the same type.
351 public int CompareTo (object obj)
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 () + ".");
368 object value1, value2;
370 value1 = this.get_value ();
371 value2 = ((Enum)obj).get_value ();
373 return ((IComparable)value1).CompareTo (value2);
376 public override string ToString ()
378 return ToString ("G", null);
381 public string ToString (IFormatProvider provider)
383 return ToString ("G", provider);
386 public string ToString (String format)
388 return ToString (format, null);
391 public string ToString (String format, IFormatProvider provider)
393 // provider is not used for Enums
395 if (format == String.Empty || format == null) {
398 return Format (this.GetType(), this.get_value (), format);
401 public static object ToObject (Type enumType, byte value)
403 return ToObject (enumType, (object)value);
406 public static object ToObject (Type enumType, short value)
408 return ToObject (enumType, (object)value);
411 public static object ToObject (Type enumType, int value)
413 return ToObject (enumType, (object)value);
416 public static object ToObject (Type enumType, long value)
418 return ToObject (enumType, (object)value);
421 [MethodImplAttribute(MethodImplOptions.InternalCall)]
422 public static extern object ToObject (Type enumType, object value);
424 [CLSCompliant (false)]
425 public static object ToObject (Type enumType, sbyte value)
427 return ToObject (enumType, (object)value);
430 [CLSCompliant (false)]
431 public static object ToObject (Type enumType, ushort value)
433 return ToObject (enumType, (object)value);
436 [CLSCompliant (false)]
437 public static object ToObject (Type enumType, uint value)
439 return ToObject (enumType, (object)value);
442 [CLSCompliant (false)]
443 public static object ToObject (Type enumType, ulong value)
445 return ToObject (enumType, (object)value);
448 public override bool Equals (object obj)
450 if (obj == null || !(obj is Enum))
453 if (obj.GetType() != this.GetType ())
456 object v1 = this.get_value ();
457 object v2 = ((Enum)obj).get_value ();
459 return v1.Equals (v2);
462 public override int GetHashCode ()
464 object v = this.get_value ();
465 return v.GetHashCode ();
468 private static string FormatSpecifier_X (Type enumType, object value)
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";
474 switch (Type.GetTypeCode (enumType)) {
476 // Char doesn't support ToString(format), so convert to an int and
478 char v = (char) value;
479 return Convert.ToInt32 (v).ToString(format);
481 return ((sbyte)value).ToString (format);
483 return ((byte)value).ToString (format);
485 return ((short)value).ToString (format);
486 case TypeCode.UInt16:
487 return ((ushort)value).ToString (format);
489 return ((int)value).ToString (format);
490 case TypeCode.UInt32:
491 return ((uint)value).ToString (format);
493 return ((long)value).ToString (format);
494 case TypeCode.UInt64:
495 return ((ulong)value).ToString (format);
497 throw new Exception ("Invalid type code for enumeration.");
501 static string FormatFlags (Type enumType, object value)
505 MonoEnumInfo.GetInfo (enumType, out info);
506 string asString = value.ToString ();
507 if (asString == "0") {
508 retVal = GetName (enumType, value);
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;
519 for (int i = info.values.Length - 1; i >= 0; i--) {
520 enumValue = (sbyte) info.values.GetValue (i);
521 if (i == 0 && enumValue == 0)
524 if ((flags & enumValue) == enumValue) {
525 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
529 if (flags != 0) return asString;
532 case TypeCode.Byte: {
533 byte flags = (byte) value;
535 for (int i = info.values.Length - 1; i >= 0; i--) {
536 enumValue = (byte) info.values.GetValue (i);
537 if (i == 0 && enumValue == 0)
540 if ((flags & enumValue) == enumValue) {
541 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
545 if (flags != 0) return asString;
548 case TypeCode.Int16: {
549 short flags = (short) value;
551 for (int i = info.values.Length - 1; i >= 0; i--) {
552 enumValue = (short) info.values.GetValue (i);
553 if (i == 0 && enumValue == 0)
556 if ((flags & enumValue) == enumValue) {
557 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
561 if (flags != 0) return asString;
564 case TypeCode.Int32: {
565 int flags = (int) value;
567 for (int i = info.values.Length - 1; i >= 0; i--) {
568 enumValue = (int) info.values.GetValue (i);
569 if (i == 0 && enumValue == 0)
572 if ((flags & enumValue) == enumValue) {
573 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
577 if (flags != 0) return asString;
580 case TypeCode.UInt16: {
581 ushort flags = (ushort) value;
583 for (int i = info.values.Length - 1; i >= 0; i--) {
584 enumValue = (ushort) info.values.GetValue (i);
585 if (i == 0 && enumValue == 0)
588 if ((flags & enumValue) == enumValue) {
589 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
593 if (flags != 0) return asString;
596 case TypeCode.UInt32: {
597 uint flags = (uint) value;
599 for (int i = info.values.Length - 1; i >= 0; i--) {
600 enumValue = (uint) info.values.GetValue (i);
601 if (i == 0 && enumValue == 0)
604 if ((flags & enumValue) == enumValue) {
605 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
609 if (flags != 0) return asString;
612 case TypeCode.Int64: {
613 long flags = (long) value;
615 for (int i = info.values.Length - 1; i >= 0; i--) {
616 enumValue = (long) info.values.GetValue (i);
617 if (i == 0 && enumValue == 0)
620 if ((flags & enumValue) == enumValue) {
621 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
625 if (flags != 0) return asString;
628 case TypeCode.UInt64: {
629 ulong flags = (ulong) value;
631 for (int i = info.values.Length - 1; i >= 0; i--) {
632 enumValue = (ulong) info.values.GetValue (i);
633 if (i == 0 && enumValue == 0)
636 if ((flags & enumValue) == enumValue) {
637 retVal = info.names[i] + (retVal == String.Empty ? "" : ", ") + retVal;
641 if (flags != 0) return asString;
652 public static string Format (Type enumType, object value, string format)
654 if (enumType == null)
655 throw new ArgumentNullException ("enumType");
657 throw new ArgumentNullException ("value");
659 throw new ArgumentNullException ("format");
661 if (!enumType.IsEnum)
662 throw new ArgumentException ("enumType is not an Enum Type.");
664 Type vType = value.GetType();
665 if (vType != enumType && vType != Enum.GetUnderlyingType (enumType))
666 throw new ArgumentException ();
668 if (format.Length != 1)
669 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," +
670 "\"x\",\"F\",\"f\",\"D\" or \"d\".");
672 char formatChar = format [0];
674 if ((formatChar == 'G' || formatChar == 'g')) {
675 if (!Attribute.IsDefined (enumType, typeof(FlagsAttribute))) {
676 retVal = GetName (enumType, value);
678 retVal = value.ToString();
686 if ((formatChar == 'f' || formatChar == 'F'))
687 return FormatFlags (enumType, value);
690 switch (formatChar) {
693 retVal = FormatSpecifier_X (enumType, value);
697 if (Enum.GetUnderlyingType (enumType) == typeof (ulong)) {
698 ulong ulongValue = Convert.ToUInt64 (value);
699 retVal = ulongValue.ToString ();
701 long longValue = Convert.ToInt64 (value);
702 retVal = longValue.ToString ();
706 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," +
707 "\"x\",\"F\",\"f\",\"D\" or \"d\".");