BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System / System.ComponentModel / EnumConverter.cs
1 //
2 // System.ComponentModel.EnumConverter.cs
3 //
4 // Authors:
5 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //   Ivan N. Zlatev (contact@i-nz.net)
8 //
9 // (C) 2002 Ximian, Inc (http://www.ximian.com)
10 // (C) 2003 Andreas Nahr
11 // (C) 2007 Ivan N. Zlatev
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 using System;
35 using System.Collections;
36 using System.Globalization;
37 using System.Reflection;
38 using System.ComponentModel.Design.Serialization;
39
40 namespace System.ComponentModel
41 {
42         public class EnumConverter : TypeConverter
43         {
44                 private Type type;
45                 private StandardValuesCollection stdValues;
46
47                 public EnumConverter (Type type)
48                 {
49                         this.type = type;
50                 }
51
52                 public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
53                 {
54                         if (destinationType == typeof (InstanceDescriptor))
55                                 return true;
56
57 #if NET_2_0
58                         if (destinationType == typeof (Enum[]))
59                                 return true;
60 #endif
61
62                         return base.CanConvertTo (context, destinationType);
63                 }
64
65                 public override object ConvertTo (ITypeDescriptorContext context,
66                                                   CultureInfo culture,
67                                                   object value,
68                                                   Type destinationType)
69                 {
70                         if (destinationType == typeof (string) && value != null) {
71                                 // we need to be able to convert enum names,
72                                 // integral values and other enum fields
73                                 if (value is IConvertible) {
74                                         Type underlyingType = Enum.GetUnderlyingType (type);
75                                         if (underlyingType != value.GetType ()) {
76                                                 value = ((IConvertible) value).ToType (
77                                                         underlyingType, culture);
78                                         }
79                                 }
80
81                                 if (!IsFlags && !IsValid (context, value))
82                                         throw CreateValueNotValidException (value);
83
84                                 return Enum.Format (type, value, "G");
85                         } else if (destinationType == typeof (InstanceDescriptor) && value != null) {
86                                 string enumString = ConvertToString (context, culture, value);
87                                 if (IsFlags && enumString.IndexOf (",") != -1) {
88                                         if (value is IConvertible) {
89                                                 Type underlyingType = Enum.GetUnderlyingType (type);
90                                                 object enumValue = ((IConvertible)value).ToType (underlyingType, culture);
91                                                 MethodInfo toObjectMethod = typeof (Enum).GetMethod ("ToObject", new Type[] { typeof (Type), 
92                                                                                                                                                           underlyingType });
93                                                 return new InstanceDescriptor (toObjectMethod, new object[] { type, enumValue });
94                                         }
95                                 } else {
96                                         FieldInfo f = type.GetField (enumString);
97                                         if (f != null)
98                                                 return new InstanceDescriptor (f, null);
99                                 }
100 #if NET_2_0
101                         } else if (destinationType == typeof (Enum[]) && value != null) {
102                                 if (!IsFlags) {
103                                         return new Enum[] { (Enum) Enum.ToObject (type, value) };
104                                 } else {
105                                         long valueLong = Convert.ToInt64 (
106                                                 (Enum) value, culture);
107                                         Array enumValArray = Enum.GetValues (type);
108                                         long[] enumValues = new long[enumValArray.Length];
109                                         for (int i=0; i < enumValArray.Length; i++)
110                                                 enumValues[i] = Convert.ToInt64 (enumValArray.GetValue (i));
111
112                                         ArrayList enums = new ArrayList ();
113                                         bool interrupt = false;
114                                         while (!interrupt) {
115                                                 // interrupt the loop unless we find a match to avoid
116                                                 // looping indefinitely
117                                                 interrupt = true;
118                                                 foreach (long val in enumValues) {
119                                                         if ((val != 0 && ((val & valueLong) == val)) || val == valueLong) {
120                                                                 enums.Add (Enum.ToObject (type, val));
121                                                                 valueLong &= (~val);
122                                                                 interrupt = false;
123                                                         }
124                                                 }
125                                                 if (valueLong == 0) // nothing left to do
126                                                         interrupt = true;
127                                         }
128
129                                         // add item for remainder
130                                         if (valueLong != 0)
131                                                 enums.Add (Enum.ToObject (type, valueLong));
132
133                                         return enums.ToArray (typeof(Enum));
134                                 }
135 #endif
136                         }
137                         
138                         return base.ConvertTo (context, culture, value, destinationType);
139                 }
140
141                 public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
142                 {
143                         if (sourceType == typeof (string))
144                                 return true;
145
146 #if NET_2_0
147                         if (sourceType == typeof (Enum[]))
148                                 return true;
149 #endif
150
151                         return base.CanConvertFrom (context, sourceType);
152                 }
153
154                 public override object ConvertFrom (ITypeDescriptorContext context,
155                                                     CultureInfo culture,
156                                                     object value)
157                 {
158                         if (value is string) {
159                                 string name = value as string;
160 #if NET_2_0
161                                 try {
162 #endif
163                                         if (name.IndexOf (',') == -1)
164                                                 return Enum.Parse (type, name, true);
165
166                                         long val = 0;
167                                         string [] names = name.Split (',');
168                                         foreach (string n in names) {
169                                                 Enum e = (Enum) Enum.Parse (type, n, true);
170                                                 val |= Convert.ToInt64 (e, culture);
171                                         }
172                                         return Enum.ToObject (type, val);
173 #if NET_2_0
174                                 } catch (Exception ex) {
175                                         throw new FormatException (name + " is " +
176                                                 "not a valid value for " +
177                                                 type.Name, ex);
178                                 }
179 #endif
180 #if NET_2_0
181                         } else if (value is Enum[]) {
182                                 long val = 0;
183                                 foreach (Enum e in (Enum[])value)
184                                         val |= Convert.ToInt64 (e, culture);
185                                 return Enum.ToObject (type, val);
186 #endif
187                         }
188
189                         return base.ConvertFrom (context, culture, value);
190                 }
191
192                 public override bool IsValid (ITypeDescriptorContext context, object value)
193                 {
194                         return Enum.IsDefined (type, value);
195                 }
196
197                 public override bool GetStandardValuesSupported (ITypeDescriptorContext context)
198                 {
199                         return true;
200                 }
201
202                 public override bool GetStandardValuesExclusive (ITypeDescriptorContext context)
203                 {
204                         return !IsFlags;
205                 }
206
207                 public override StandardValuesCollection GetStandardValues (ITypeDescriptorContext context)
208                 {
209                         if (stdValues == null) {
210                                 Array values = Enum.GetValues (type);
211                                 Array.Sort (values);
212                                 stdValues = new StandardValuesCollection (values);
213                         }
214                         return stdValues;
215                 }
216
217                 protected virtual IComparer Comparer {
218                         get { return new EnumConverter.EnumComparer (); }
219                 }
220
221                 protected Type EnumType {
222                         get { return type; }
223                 }
224
225                 protected TypeConverter.StandardValuesCollection Values {
226                         get { return stdValues;  }
227                         set { stdValues = value; }
228                 }
229
230                 ArgumentException CreateValueNotValidException (object value)
231                 {
232                         string msg = string.Format (CultureInfo.InvariantCulture,
233                                 "The value '{0}' is not a valid value for the " +
234                                 "enum '{1}'", value, type.Name);
235                         return new ArgumentException (msg);
236                 }
237
238                 bool IsFlags {
239                         get {
240                                 return (type.IsDefined (typeof (FlagsAttribute),
241                                         false));
242                         }
243                 }
244
245                 private class EnumComparer : IComparer
246                 {
247                         int IComparer.Compare (object compareObject1, object compareObject2) 
248                         {
249                                 string CompareString1 = (compareObject1 as string);
250                                 string CompareString2 = (compareObject2 as string);
251                                 if ((CompareString1 == null) || (CompareString2 == null))
252                                         return Collections.Comparer.Default.Compare (compareObject1, compareObject2);
253                                 return CultureInfo.InvariantCulture.CompareInfo.Compare (CompareString1, CompareString2);
254                         }
255                 }
256         }
257 }