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