1 //------------------------------------------------------------------------------ 2 // <copyright file="ConfigurationProperty.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 using System; 8 using System.ComponentModel; 9 using System.Security.Permissions; 10 using System.Collections.Generic; 11 using System.Collections.Specialized; 12 using System.Reflection; 13 using System.Text; 14 using System.Diagnostics.CodeAnalysis; 15 16 namespace System.Configuration { 17 18 public sealed class ConfigurationProperty { 19 internal static readonly ConfigurationValidatorBase NonEmptyStringValidator = new StringValidator(1); 20 private readonly static ConfigurationValidatorBase DefaultValidatorInstance = new DefaultValidator(); 21 internal static readonly string DefaultCollectionPropertyName = ""; 22 private string _name; 23 private string _providedName; 24 private string _description; 25 private Type _type; 26 private Object _defaultValue; 27 private TypeConverter _converter; 28 private ConfigurationPropertyOptions _options; 29 private ConfigurationValidatorBase _validator; 30 private String _addElementName = null; 31 private String _removeElementName = null; 32 private String _clearElementName = null; 33 private volatile bool _isTypeInited; 34 private volatile bool _isConfigurationElementType; 35 ConfigurationProperty(String name, Type type)36 public ConfigurationProperty(String name, Type type) { 37 object defaultValue = null; 38 39 ConstructorInit(name, type, ConfigurationPropertyOptions.None, null, null); 40 41 if (type == typeof(string)) { 42 defaultValue = String.Empty; 43 } 44 else if (type.IsValueType) { 45 defaultValue = TypeUtil.CreateInstanceWithReflectionPermission(type); 46 } 47 SetDefaultValue(defaultValue); 48 } 49 ConfigurationProperty(String name, Type type, Object defaultValue)50 public ConfigurationProperty(String name, Type type, Object defaultValue) 51 : this(name, type, defaultValue, ConfigurationPropertyOptions.None) { 52 } 53 ConfigurationProperty(String name, Type type, Object defaultValue, ConfigurationPropertyOptions options)54 public ConfigurationProperty(String name, Type type, Object defaultValue, ConfigurationPropertyOptions options) 55 : this(name, type, defaultValue, null, null, options) { 56 } 57 ConfigurationProperty(String name, Type type, Object defaultValue, TypeConverter typeConverter, ConfigurationValidatorBase validator, ConfigurationPropertyOptions options)58 public ConfigurationProperty(String name, 59 Type type, 60 Object defaultValue, 61 TypeConverter typeConverter, 62 ConfigurationValidatorBase validator, 63 ConfigurationPropertyOptions options) 64 : this(name, type, defaultValue, typeConverter, validator, options, null) { 65 } 66 ConfigurationProperty(String name, Type type, Object defaultValue, TypeConverter typeConverter, ConfigurationValidatorBase validator, ConfigurationPropertyOptions options, string description)67 public ConfigurationProperty(String name, 68 Type type, 69 Object defaultValue, 70 TypeConverter typeConverter, 71 ConfigurationValidatorBase validator, 72 ConfigurationPropertyOptions options, 73 string description) { 74 ConstructorInit(name, type, options, validator, typeConverter); 75 76 SetDefaultValue(defaultValue); 77 } 78 ConfigurationProperty(PropertyInfo info)79 internal ConfigurationProperty(PropertyInfo info) { 80 Debug.Assert(info != null, "info != null"); 81 82 // Bellow are the attributes we handle 83 TypeConverterAttribute attribConverter = null; 84 ConfigurationPropertyAttribute attribProperty = null; 85 ConfigurationValidatorAttribute attribValidator = null; 86 87 // Compatability attributes 88 // If the approprite data is provided in the ConfigPropAttribute then the one bellow will be ignored 89 DescriptionAttribute attribStdDescription = null; 90 DefaultValueAttribute attribStdDefault = null; 91 92 TypeConverter typeConverter = null; 93 ConfigurationValidatorBase validator = null; 94 95 // Find the interesting attributes in the collection 96 foreach (Attribute attribute in Attribute.GetCustomAttributes(info)) { 97 if (attribute is TypeConverterAttribute) { 98 attribConverter = (TypeConverterAttribute)attribute; 99 typeConverter = TypeUtil.CreateInstanceRestricted<TypeConverter>(info.DeclaringType, attribConverter.ConverterTypeName); 100 } 101 else if (attribute is ConfigurationPropertyAttribute) { 102 attribProperty = (ConfigurationPropertyAttribute)attribute; 103 } 104 else if (attribute is ConfigurationValidatorAttribute) { 105 // There could be more then one validator attribute specified on a property 106 // Currently we consider this an error since it's too late to fix it for whidbey 107 // but the right thing to do is to introduce new validator type ( CompositeValidator ) that is a list of validators and executes 108 // them all 109 110 if (validator != null) { 111 throw new ConfigurationErrorsException(SR.GetString(SR.Validator_multiple_validator_attributes, info.Name)); 112 } 113 114 attribValidator = (ConfigurationValidatorAttribute)attribute; 115 attribValidator.SetDeclaringType(info.DeclaringType); 116 validator = attribValidator.ValidatorInstance; 117 } 118 else if (attribute is DescriptionAttribute) { 119 attribStdDescription = (DescriptionAttribute)attribute; 120 } 121 else if (attribute is DefaultValueAttribute) { 122 attribStdDefault = (DefaultValueAttribute)attribute; 123 } 124 125 } 126 127 Type propertyType = info.PropertyType; 128 // Collections need some customization when the collection attribute is present 129 if (typeof(ConfigurationElementCollection).IsAssignableFrom(propertyType)) { 130 ConfigurationCollectionAttribute attribCollection = 131 Attribute.GetCustomAttribute(info, 132 typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; 133 134 // If none on the property - see if there is an attribute on the collection type itself 135 if (attribCollection == null) { 136 attribCollection = 137 Attribute.GetCustomAttribute(propertyType, 138 typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; 139 } 140 if (attribCollection != null) { 141 if (attribCollection.AddItemName.IndexOf(',') == -1) { 142 _addElementName = attribCollection.AddItemName; 143 } 144 _removeElementName = attribCollection.RemoveItemName; 145 _clearElementName = attribCollection.ClearItemsName; 146 147 } 148 149 } 150 151 // This constructor shouldnt be invoked if the reflection info is not for an actual config property 152 Debug.Assert(attribProperty != null, "attribProperty != null"); 153 154 ConstructorInit(attribProperty.Name, 155 info.PropertyType, 156 attribProperty.Options, 157 validator, 158 typeConverter); 159 160 // Figure out the default value 161 InitDefaultValueFromTypeInfo(attribProperty, attribStdDefault); 162 163 // Get the description 164 if ((attribStdDescription != null) && !string.IsNullOrEmpty(attribStdDescription.Description)) { 165 _description = attribStdDescription.Description; 166 } 167 } 168 ConstructorInit(string name, Type type, ConfigurationPropertyOptions options, ConfigurationValidatorBase validator, TypeConverter converter)169 private void ConstructorInit(string name, 170 Type type, 171 ConfigurationPropertyOptions options, 172 ConfigurationValidatorBase validator, 173 TypeConverter converter) { 174 if (typeof(ConfigurationSection).IsAssignableFrom(type)) { 175 throw new ConfigurationErrorsException(SR.GetString(SR.Config_properties_may_not_be_derived_from_configuration_section, name)); 176 } 177 178 _providedName = name; // save the provided name so we can check for default collection names 179 if ((options & ConfigurationPropertyOptions.IsDefaultCollection) != 0 && 180 String.IsNullOrEmpty(name)) { 181 name = DefaultCollectionPropertyName; 182 } 183 else { 184 ValidatePropertyName(name); 185 } 186 187 _name = name; 188 _type = type; 189 _options = options; 190 _validator = validator; 191 _converter = converter; 192 193 // Use the default validator if none was supplied 194 if (_validator == null) { 195 _validator = DefaultValidatorInstance; 196 } 197 else { 198 // Make sure the supplied validator supports the type of this property 199 if (!_validator.CanValidate(_type)) { 200 throw new ConfigurationErrorsException(SR.GetString(SR.Validator_does_not_support_prop_type, _name)); 201 } 202 } 203 } 204 ValidatePropertyName(string name)205 private void ValidatePropertyName(string name) { 206 if (string.IsNullOrEmpty(name)) { 207 throw new ArgumentException(SR.GetString(SR.String_null_or_empty), "name"); 208 } 209 210 if (BaseConfigurationRecord.IsReservedAttributeName(name)) { 211 throw new ArgumentException(SR.GetString(SR.Property_name_reserved, name)); 212 } 213 } 214 SetDefaultValue(object value)215 private void SetDefaultValue(object value) { 216 // Validate the default value if any. This should make errors from invalid defaults easier to catch 217 if (value != null && value != ConfigurationElement.s_nullPropertyValue) { 218 bool canAssign = _type.IsAssignableFrom(value.GetType()); 219 if (!canAssign && this.Converter.CanConvertFrom(value.GetType())) { 220 value = this.Converter.ConvertFrom(value); 221 } 222 else if (!canAssign) { 223 throw new ConfigurationErrorsException(SR.GetString(SR.Default_value_wrong_type, _name)); 224 } 225 226 Validate(value); 227 228 _defaultValue = value; 229 } 230 } 231 InitDefaultValueFromTypeInfo(ConfigurationPropertyAttribute attribProperty, DefaultValueAttribute attribStdDefault)232 private void InitDefaultValueFromTypeInfo(ConfigurationPropertyAttribute attribProperty, 233 DefaultValueAttribute attribStdDefault) { 234 object defaultValue = attribProperty.DefaultValue; 235 236 // If there is no default value there - try the other attribute ( the clr standard one ) 237 if ((defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) && 238 (attribStdDefault != null)) { 239 defaultValue = attribStdDefault.Value; 240 } 241 242 // If there was a default value in the prop attribute - check if we need to convert it from string 243 if ((defaultValue != null) && (defaultValue is string) && (_type != typeof(string))) { 244 // Use the converter to parse this property default value 245 try { 246 defaultValue = Converter.ConvertFromInvariantString((string)defaultValue); 247 } 248 catch (Exception ex) { 249 throw new ConfigurationErrorsException(SR.GetString(SR.Default_value_conversion_error_from_string, _name, ex.Message)); 250 } 251 } 252 if (defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) { 253 if (_type == typeof(string)) { 254 defaultValue = String.Empty; 255 } 256 else if (_type.IsValueType) { 257 defaultValue = TypeUtil.CreateInstanceWithReflectionPermission(_type); 258 } 259 } 260 SetDefaultValue(defaultValue); 261 } 262 263 public string Name { 264 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_name is actually immutable once constructed")] 265 get { 266 return _name; 267 } 268 } 269 270 public string Description { 271 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_description is actually immutable once constructed")] 272 get { 273 return _description; 274 } 275 } 276 277 internal string ProvidedName { 278 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_providedName is actually immutable once constructed")] 279 get { return _providedName; } 280 } 281 282 internal bool IsConfigurationElementType { 283 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_type is effectively readonly")] 284 get { 285 if (!_isTypeInited) { 286 _isConfigurationElementType = typeof(ConfigurationElement).IsAssignableFrom(_type); 287 _isTypeInited = true; 288 } 289 return _isConfigurationElementType; 290 } 291 } 292 293 public Type Type { 294 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_type is actually immutable once constructed")] 295 get { 296 return _type; 297 } 298 } 299 300 public Object DefaultValue { 301 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_defaultValue is actually immutable once constructed")] 302 get { 303 return _defaultValue; 304 } 305 } 306 307 public bool IsRequired { 308 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 309 get { 310 return (_options & ConfigurationPropertyOptions.IsRequired) != 0; 311 } 312 } 313 314 public bool IsKey { 315 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 316 get { 317 return (_options & ConfigurationPropertyOptions.IsKey) != 0; 318 } 319 } 320 321 public bool IsDefaultCollection { 322 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 323 get { 324 return ((_options & ConfigurationPropertyOptions.IsDefaultCollection) != 0); 325 } 326 } 327 328 public bool IsTypeStringTransformationRequired { 329 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 330 get { 331 return (_options & ConfigurationPropertyOptions.IsTypeStringTransformationRequired) != 0; 332 } 333 } 334 335 public bool IsAssemblyStringTransformationRequired { 336 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 337 get { 338 return (_options & ConfigurationPropertyOptions.IsAssemblyStringTransformationRequired) != 0; 339 } 340 } 341 342 public bool IsVersionCheckRequired { 343 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_options is actually immutable once constructed")] 344 get { 345 return (_options & ConfigurationPropertyOptions.IsVersionCheckRequired) != 0; 346 } 347 } 348 349 public TypeConverter Converter { 350 get { 351 CreateConverter(); 352 353 return _converter; 354 } 355 } 356 357 public ConfigurationValidatorBase Validator { 358 get { 359 return _validator; 360 } 361 } 362 363 internal String AddElementName { 364 get { 365 return _addElementName; 366 } 367 } 368 internal String RemoveElementName { 369 get { 370 return _removeElementName; 371 } 372 } 373 internal String ClearElementName { 374 get { 375 return _clearElementName; 376 } 377 } 378 ConvertFromString(string value)379 internal Object ConvertFromString(string value) { 380 object result = null; 381 382 try { 383 result = Converter.ConvertFromInvariantString(value); 384 } 385 catch (Exception ex) { 386 throw new ConfigurationErrorsException(SR.GetString(SR.Top_level_conversion_error_from_string, _name, ex.Message)); 387 } 388 389 return result; 390 } 391 ConvertToString(Object value)392 internal string ConvertToString(Object value) { 393 string result = null; 394 395 try { 396 if (_type == typeof(bool)) { 397 result = ((bool)value) ? "true" : "false"; // the converter will break 1.1 compat for bool 398 } 399 else { 400 result = Converter.ConvertToInvariantString(value); 401 } 402 } 403 catch (Exception ex) { 404 throw new ConfigurationErrorsException(SR.GetString(SR.Top_level_conversion_error_to_string, _name, ex.Message)); 405 } 406 407 return result; 408 } 409 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "_name and _validator are effectively readonly")] Validate(object value)410 internal void Validate(object value) { 411 try { 412 _validator.Validate(value); 413 } 414 catch (Exception ex) { 415 throw new ConfigurationErrorsException(SR.GetString(SR.Top_level_validation_error, _name, ex.Message),ex); 416 } 417 } CreateConverter()418 private void CreateConverter() { 419 // Some properties cannot have type converters. 420 // Such examples are properties that are ConfigurationElement ( derived classes ) 421 // or properties which are user-defined and the user code handles serialization/desirialization so 422 // the property itself is never converted to/from string 423 424 if (_converter == null) { 425 // Enums are exception. We use our custom converter for all enums 426 if (_type.IsEnum) { 427 _converter = new GenericEnumConverter(_type); 428 } 429 else if (!_type.IsSubclassOf(typeof(ConfigurationElement))) { 430 _converter = TypeDescriptor.GetConverter(_type); 431 432 if ((_converter == null) || 433 !_converter.CanConvertFrom(typeof(String)) || 434 !_converter.CanConvertTo(typeof(String))) { 435 throw new ConfigurationErrorsException(SR.GetString(SR.No_converter, _name, _type.Name)); 436 } 437 } 438 } 439 } 440 } 441 } 442