1 //------------------------------------------------------------------------------
2 // <copyright file="EnumConverter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  */
9 namespace System.ComponentModel {
10     using Microsoft.Win32;
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.ComponentModel.Design.Serialization;
14     using System.Globalization;
15     using System.Diagnostics;
16     using System.Reflection;
17     using System.Runtime.Serialization.Formatters;
18     using System.Runtime.Remoting;
19     using System.Runtime.InteropServices;
20     using System.Security.Permissions;
21 
22     /// <devdoc>
23     /// <para>Provides a type converter to convert <see cref='System.Enum'/>
24     /// objects to and from various
25     /// other representations.</para>
26     /// </devdoc>
27     [HostProtection(SharedState = true)]
28     public class EnumConverter : TypeConverter {
29         /// <devdoc>
30         ///    <para>
31         ///       Provides a <see cref='System.ComponentModel.TypeConverter.StandardValuesCollection'/> that specifies the
32         ///       possible values for the enumeration.
33         ///    </para>
34         /// </devdoc>
35         private StandardValuesCollection values;
36         /// <devdoc>
37         ///    <para>
38         ///       Specifies
39         ///       the
40         ///       type of the enumerator this converter is
41         ///       associated with.
42         ///    </para>
43         /// </devdoc>
44         private Type type;
45 
46         /// <devdoc>
47         ///    <para>
48         ///       Initializes a new instance of the <see cref='System.ComponentModel.EnumConverter'/> class for the given
49         ///       type.
50         ///    </para>
51         /// </devdoc>
EnumConverter(Type type)52         public EnumConverter(Type type) {
53             this.type = type;
54         }
55 
56         /// <devdoc>
57         ///    <para>[To be supplied.]</para>
58         /// </devdoc>
59         protected Type EnumType {
60             get {
61                 return type;
62             }
63         }
64 
65         /// <devdoc>
66         ///    <para>[To be supplied.]</para>
67         /// </devdoc>
68         protected StandardValuesCollection Values {
69             get {
70                 return values;
71             }
72             set {
73                 values = value;
74             }
75         }
76 
77         /// <internalonly/>
78         /// <devdoc>
79         ///    <para>Gets a value indicating whether this converter
80         ///       can convert an object in the given source type to an enumeration object using
81         ///       the specified context.</para>
82         /// </devdoc>
CanConvertFrom(ITypeDescriptorContext context, Type sourceType)83         public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
84             if (sourceType == typeof(string) || sourceType == typeof(Enum[])) {
85                 return true;
86             }
87             return base.CanConvertFrom(context, sourceType);
88         }
89 
90         /// <devdoc>
91         ///    <para>Gets a value indicating whether this converter can
92         ///       convert an object to the given destination type using the context.</para>
93         /// </devdoc>
CanConvertTo(ITypeDescriptorContext context, Type destinationType)94         public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
95             if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(Enum[])) {
96                 return true;
97             }
98             return base.CanConvertTo(context, destinationType);
99         }
100 
101         /// <devdoc>
102         /// <para>Gets an <see cref='System.Collections.IComparer'/>
103         /// interface that can
104         /// be used to sort the values of the enumerator.</para>
105         /// </devdoc>
106         protected virtual IComparer Comparer {
107             get {
108                 return InvariantComparer.Default;
109             }
110         }
111 
112         /// <internalonly/>
113         /// <devdoc>
114         ///    <para>Converts the specified value object to an enumeration object.</para>
115         /// </devdoc>
ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)116         public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
117             if (value is string) {
118                 try {
119                     string strValue = (string)value;
120                     if (strValue.IndexOf(',') != -1) {
121                         long convertedValue = 0;
122                         string[] values = strValue.Split(new char[] {','});
123                         foreach(string v in values) {
124                             convertedValue |= Convert.ToInt64((Enum)Enum.Parse(type, v, true), culture);
125                         }
126                         return Enum.ToObject(type, convertedValue);
127                     }
128                     else {
129                         return Enum.Parse(type, strValue, true);
130                     }
131                 }
132                 catch (Exception e) {
133                     throw new FormatException(SR.GetString(SR.ConvertInvalidPrimitive, (string)value, type.Name), e);
134                 }
135             }
136             else if (value is Enum[]) {
137                 long finalValue = 0;
138                 foreach(Enum e in (Enum[])value) {
139                     finalValue |= Convert.ToInt64(e, culture);
140                 }
141                 return Enum.ToObject(type, finalValue);
142             }
143             return base.ConvertFrom(context, culture, value);
144         }
145 
146         /// <internalonly/>
147         /// <devdoc>
148         ///    <para>Converts the given
149         ///       value object to the
150         ///       specified destination type.</para>
151         /// </devdoc>
152         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")]  // Keep call to Enum.IsDefined
ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)153         public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
154             if (destinationType == null) {
155                 throw new ArgumentNullException("destinationType");
156             }
157 
158             if (destinationType == typeof(string) && value != null) {
159                 // Raise an argument exception if the value isn't defined and if
160                 // the enum isn't a flags style.
161                 //
162                 Type underlyingType = Enum.GetUnderlyingType(type);
163                 if (value is IConvertible && value.GetType() != underlyingType) {
164                     value = ((IConvertible)value).ToType(underlyingType, culture);
165                 }
166                 if (!type.IsDefined(typeof(FlagsAttribute), false) && !Enum.IsDefined(type, value)) {
167                     throw new ArgumentException(SR.GetString(SR.EnumConverterInvalidValue, value.ToString(), type.Name));
168                 }
169 
170                 return Enum.Format(type, value, "G");
171             }
172             if (destinationType == typeof(InstanceDescriptor) && value != null) {
173                 string enumName = ConvertToInvariantString(context, value);
174 
175                 if (type.IsDefined(typeof(FlagsAttribute), false) && enumName.IndexOf(',') != -1) {
176                     // This is a flags enum, and there is no one flag
177                     // that covers the value.  Instead, convert the
178                     // value to the underlying type and invoke
179                     // a ToObject call on enum.
180                     //
181                     Type underlyingType = Enum.GetUnderlyingType(type);
182                     if (value is IConvertible) {
183                         object convertedValue = ((IConvertible)value).ToType(underlyingType, culture);
184 
185                         MethodInfo method = typeof(Enum).GetMethod("ToObject", new Type[] {typeof(Type), underlyingType});
186                         if (method != null) {
187                             return new InstanceDescriptor(method, new object[] {type, convertedValue});
188                         }
189                     }
190                 }
191                 else {
192                     FieldInfo info = type.GetField(enumName);
193                     if (info != null) {
194                         return new InstanceDescriptor(info, null);
195                     }
196                 }
197             }
198             if (destinationType == typeof(Enum[]) && value != null) {
199                 if (type.IsDefined(typeof(FlagsAttribute), false)) {
200                     List<Enum> flagValues = new List<Enum>();
201 
202                     Array objValues = Enum.GetValues(type);
203                     long[] ulValues = new long[objValues.Length];
204                     for(int idx = 0; idx < objValues.Length; idx++) {
205                         ulValues[idx] = Convert.ToInt64((Enum)objValues.GetValue(idx), culture);
206                     }
207 
208                     long longValue = Convert.ToInt64((Enum)value, culture);
209                     bool valueFound = true;
210                     while(valueFound) {
211                         valueFound = false;
212                         foreach(long ul in ulValues) {
213                             if ((ul != 0 && (ul & longValue) == ul) || ul == longValue) {
214                                 flagValues.Add((Enum)Enum.ToObject(type, ul));
215                                 valueFound = true;
216                                 longValue &= ~ul;
217                                 break;
218                             }
219                         }
220 
221                         if (longValue == 0) {
222                             break;
223                         }
224                     }
225 
226                     if (!valueFound && longValue != 0) {
227                         flagValues.Add((Enum)Enum.ToObject(type, longValue));
228                     }
229 
230                     return flagValues.ToArray();
231                 }
232                 else {
233                     return new Enum[] {(Enum)Enum.ToObject(type, value)};
234                 }
235             }
236 
237             return base.ConvertTo(context, culture, value, destinationType);
238         }
239 
240         /// <internalonly/>
241         /// <devdoc>
242         ///    <para>Gets a collection of standard values for the data type this validator is
243         ///       designed for.</para>
244         /// </devdoc>
GetStandardValues(ITypeDescriptorContext context)245         public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
246             if (values == null) {
247                 // We need to get the enum values in this rather round-about way so we can filter
248                 // out fields marked Browsable(false). Note that if multiple fields have the same value,
249                 // the behavior is undefined, since what we return are just enum values, not names.
250 
251                 Type reflectType = TypeDescriptor.GetReflectionType(type);
252                 if (reflectType == null) {
253                     reflectType = type;
254                 }
255 
256                 FieldInfo[] fields = reflectType.GetFields(BindingFlags.Public | BindingFlags.Static);
257                 ArrayList objValues = null;
258 
259                 if (fields != null && fields.Length > 0) {
260                     objValues = new ArrayList(fields.Length);
261                 }
262 
263                 if (objValues != null) {
264                     foreach (FieldInfo field in fields) {
265                         BrowsableAttribute browsableAttr = null;
266                         foreach (Attribute attr in field.GetCustomAttributes(typeof(BrowsableAttribute), false)) {
267                             browsableAttr = attr as BrowsableAttribute;
268                         }
269 
270                         if (browsableAttr == null || browsableAttr.Browsable) {
271                             object value = null;
272 
273                             try {
274                                 if (field.Name != null) {
275                                     value = Enum.Parse(type, field.Name);
276                                 }
277                             }
278                             catch (ArgumentException) {
279                                 // Hmm, for some reason, the parse threw. Let us ignore this value.
280                             }
281 
282                             if (value != null) {
283                                 objValues.Add(value);
284                             }
285                         }
286                     }
287 
288                     IComparer comparer = Comparer;
289                     if (comparer != null) {
290                         objValues.Sort(comparer);
291                     }
292                 }
293 
294                 Array arr = (objValues != null) ? objValues.ToArray() : null;
295                 values = new StandardValuesCollection(arr);
296             }
297             return values;
298         }
299 
300         /// <internalonly/>
301         /// <devdoc>
302         ///    <para>Gets a value indicating whether the list of standard values returned from
303         ///    <see cref='System.ComponentModel.TypeConverter.GetStandardValues'/>
304         ///    is an exclusive list using the specified context.</para>
305         /// </devdoc>
GetStandardValuesExclusive(ITypeDescriptorContext context)306         public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) {
307             return !type.IsDefined(typeof(FlagsAttribute), false);
308         }
309 
310         /// <internalonly/>
311         /// <devdoc>
312         ///    <para>Gets a value indicating
313         ///       whether this object
314         ///       supports a standard set of values that can be picked
315         ///       from a list using the specified context.</para>
316         /// </devdoc>
GetStandardValuesSupported(ITypeDescriptorContext context)317         public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
318             return true;
319         }
320 
321         /// <internalonly/>
322         /// <devdoc>
323         ///    <para>Gets a value indicating whether the given object value is valid for this type.</para>
324         /// </devdoc>
325         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")]  // Keep call to Enum.IsDefined
IsValid(ITypeDescriptorContext context, object value)326         public override bool IsValid(ITypeDescriptorContext context, object value) {
327             return Enum.IsDefined(type, value);
328         }
329     }
330 }
331 
332