//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Configuration.Internal; using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Security.Permissions; using System.Xml; using System.Globalization; using System.ComponentModel; using System.Security; using System.Text; namespace System.Configuration { // // Methods that are called by the configuration system, and must be overridable // by derived classes that wish to implement their own serialization/deserialization // IsModified() // ResetModified() // Reset(ConfigurationElement parentSection, object context) // DeserializeSection(object context, XmlNode xmlNode) // SerializeSection(ConfigurationElement parentSection, object context, string name) // public abstract class ConfigurationElement { private const string LockAttributesKey = "lockAttributes"; private const string LockAllAttributesExceptKey = "lockAllAttributesExcept"; private const string LockElementsKey = "lockElements"; private const string LockAll = "*"; private const string LockAllElementsExceptKey = "lockAllElementsExcept"; private const string LockItemKey = "lockItem"; internal const string DefaultCollectionPropertyName = ""; private static string[] s_lockAttributeNames = new string[] { LockAttributesKey, LockAllAttributesExceptKey, LockElementsKey, LockAllElementsExceptKey, LockItemKey, }; private static Hashtable s_propertyBags = new Hashtable(); private static volatile Dictionary s_perTypeValidators; internal static readonly Object s_nullPropertyValue = new Object(); private static ConfigurationElementProperty s_ElementProperty = new ConfigurationElementProperty(new DefaultValidator()); private bool _bDataToWrite; private bool _bModified; private bool _bReadOnly; private bool _bElementPresent; // Set to false if any part of the element is not inherited private bool _bInited; internal ConfigurationLockCollection _lockedAttributesList; internal ConfigurationLockCollection _lockedAllExceptAttributesList; internal ConfigurationLockCollection _lockedElementsList; internal ConfigurationLockCollection _lockedAllExceptElementsList; private readonly ConfigurationValues _values; private string _elementTagName; private volatile ElementInformation _evaluationElement; private ConfigurationElementProperty _elementProperty = s_ElementProperty; internal ConfigurationValueFlags _fItemLocked; internal ContextInformation _evalContext; internal BaseConfigurationRecord _configRecord; internal bool DataToWriteInternal { get { return _bDataToWrite; } set { _bDataToWrite = value; } } internal ConfigurationElement CreateElement(Type type) { // We use this.GetType() as the calling type since all code paths which lead to // CreateElement are protected methods, so inputs are provided by somebody in // the current type hierarchy. Since we expect that the most subclassed type // will be the most restricted security-wise, we'll use it as the calling type. ConfigurationElement element = (ConfigurationElement)TypeUtil.CreateInstanceRestricted(callingType: GetType(), targetType: type); element.CallInit(); return element; } protected ConfigurationElement() { _values = new ConfigurationValues(); // Set the per-type validator ( this will actually have an effect only for an attributed model elements ) // Note that in the case where the property bag fot this.GetType() has not yet been created // the validator for this instance will get applied in ApplyValidatorsRecursive ( see this.get_Properties ) ApplyValidator(this); } // Give elements that are added to a collection an opportunity to // protected internal virtual void Init() { // If Init is called by the derived class, we may be able // to set _bInited to true if the derived class properly // calls Init on its base. _bInited = true; } internal void CallInit() { // Ensure Init is called just once if (!_bInited) { Init(); _bInited = true; } } internal bool ElementPresent { get { return _bElementPresent; } set { _bElementPresent = value; } } internal string ElementTagName { get { return _elementTagName; } } internal ConfigurationLockCollection LockedAttributesList { get { return _lockedAttributesList; } } internal ConfigurationLockCollection LockedAllExceptAttributesList { get { return _lockedAllExceptAttributesList; } } internal ConfigurationValueFlags ItemLocked { get { return _fItemLocked; } } public ConfigurationLockCollection LockAttributes { get { if (_lockedAttributesList == null) { _lockedAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedAttributes); } return _lockedAttributesList; } } internal void MergeLocks(ConfigurationElement source) { if (source != null) { _fItemLocked = ((source._fItemLocked & ConfigurationValueFlags.Locked) != 0) ? (ConfigurationValueFlags.Inherited | source._fItemLocked) : _fItemLocked; if (source._lockedAttributesList != null) { if (_lockedAttributesList == null) { _lockedAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedAttributes); } foreach (string key in source._lockedAttributesList) _lockedAttributesList.Add(key, ConfigurationValueFlags.Inherited); // Mark entry as from the parent - read only } if (source._lockedAllExceptAttributesList != null) { if (_lockedAllExceptAttributesList == null) { _lockedAllExceptAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedExceptionList, String.Empty, source._lockedAllExceptAttributesList); } StringCollection intersectionCollection = IntersectLockCollections(_lockedAllExceptAttributesList, source._lockedAllExceptAttributesList); _lockedAllExceptAttributesList.ClearInternal(false); foreach (string key in intersectionCollection) { _lockedAllExceptAttributesList.Add(key, ConfigurationValueFlags.Default); } } if (source._lockedElementsList != null) { if (_lockedElementsList == null) { _lockedElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElements); } ConfigurationElementCollection collection = null; if (Properties.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[Properties.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null) { collection.internalElementTagName = source.ElementTagName; // Default collections don't know there tag name if (collection._lockedElementsList == null) { collection._lockedElementsList = _lockedElementsList; //point to the same instance of the collection from parent } } } foreach (string key in source._lockedElementsList) { _lockedElementsList.Add(key, ConfigurationValueFlags.Inherited); // Mark entry as from the parent - read only if (collection != null) { collection._lockedElementsList.Add(key, ConfigurationValueFlags.Inherited); // add the local copy } } } if (source._lockedAllExceptElementsList != null) { if (_lockedAllExceptElementsList == null || _lockedAllExceptElementsList.Count == 0) { _lockedAllExceptElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElementsExceptionList, source._elementTagName, source._lockedAllExceptElementsList); } StringCollection intersectionCollection = IntersectLockCollections(_lockedAllExceptElementsList, source._lockedAllExceptElementsList); ConfigurationElementCollection collection = null; if (Properties.DefaultCollectionProperty != null) { // this is not a collection but it may contain a default collection collection = this[Properties.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null && collection._lockedAllExceptElementsList == null) { // point default collection to the parent collection collection._lockedAllExceptElementsList = _lockedAllExceptElementsList; } } _lockedAllExceptElementsList.ClearInternal(false); foreach (string key in intersectionCollection) { if (!_lockedAllExceptElementsList.Contains(key) || key == ElementTagName) _lockedAllExceptElementsList.Add(key, ConfigurationValueFlags.Default); // add the local copy } if (_lockedAllExceptElementsList.HasParentElements) { foreach (ConfigurationProperty prop in Properties) { if ((!_lockedAllExceptElementsList.Contains(prop.Name)) && prop.IsConfigurationElementType) { ((ConfigurationElement)this[prop]).SetLocked(); } } } } } } internal void HandleLockedAttributes(ConfigurationElement source) { // if there are locked attributes on this collection element if (source != null) { if (source._lockedAttributesList != null || source._lockedAllExceptAttributesList != null) { // enumerate the possible locked properties foreach (PropertyInformation propInfo in source.ElementInformation.Properties) { if ((source._lockedAttributesList != null && (source._lockedAttributesList.Contains(propInfo.Name) || source._lockedAttributesList.Contains(LockAll))) || (source._lockedAllExceptAttributesList != null && !source._lockedAllExceptAttributesList.Contains(propInfo.Name)) ) { // if the attribute has been locked in the source then check to see // if the local config is trying to override it if (propInfo.Name != LockAttributesKey && propInfo.Name != LockAllAttributesExceptKey) { if (ElementInformation.Properties[propInfo.Name] == null) { // locked items are not defined ConfigurationPropertyCollection props = Properties; // so create the property based in the source item ConfigurationProperty prop = (ConfigurationProperty)source.Properties[propInfo.Name]; props.Add(prop); // Add the property information to the property bag _evaluationElement = null; // flush the cached element data // Add the data from the source element but mark it as in herited // This must use setvalue in order to set the lock and inherited flags ConfigurationValueFlags flags = ConfigurationValueFlags.Inherited | ConfigurationValueFlags.Locked; _values.SetValue(propInfo.Name, propInfo.Value, flags, source.PropertyInfoInternal(propInfo.Name)); } else { // don't error when optional attibute are not defined yet if (ElementInformation.Properties[propInfo.Name].ValueOrigin == PropertyValueOrigin.SetHere) { // Don't allow the override throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, propInfo.Name)); } // They did not override so we need to make sure the value comes from the locked one ElementInformation.Properties[propInfo.Name].Value = propInfo.Value; } } } } } } } // AssociateContext // // Associate a context with this element // internal virtual void AssociateContext(BaseConfigurationRecord configRecord) { _configRecord = configRecord; Values.AssociateContext(configRecord); } public /*protected internal virtual*/ ConfigurationLockCollection LockAllAttributesExcept { get { if (_lockedAllExceptAttributesList == null) { _lockedAllExceptAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedExceptionList, _elementTagName); } return _lockedAllExceptAttributesList; } } public ConfigurationLockCollection LockElements { get { if (_lockedElementsList == null) { _lockedElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElements); } return _lockedElementsList; } } public ConfigurationLockCollection LockAllElementsExcept { get { if (_lockedAllExceptElementsList == null) { _lockedAllExceptElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElementsExceptionList, _elementTagName); } return _lockedAllExceptElementsList; } } public bool LockItem { get { return ((_fItemLocked & ConfigurationValueFlags.Locked) != 0); } set { if ((_fItemLocked & ConfigurationValueFlags.Inherited) == 0) { _fItemLocked = (value == true) ? ConfigurationValueFlags.Locked : ConfigurationValueFlags.Default; _fItemLocked |= ConfigurationValueFlags.Modified; } else { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, LockItemKey)); } } } protected internal virtual bool IsModified() { if (_bModified) { return true; } if (_lockedAttributesList != null && _lockedAttributesList.IsModified) { return true; } if (_lockedAllExceptAttributesList != null && _lockedAllExceptAttributesList.IsModified) { return true; } if (_lockedElementsList != null && _lockedElementsList.IsModified) { return true; } if (_lockedAllExceptElementsList != null && _lockedAllExceptElementsList.IsModified) { return true; } if ((_fItemLocked & ConfigurationValueFlags.Modified) != 0) { return true; } foreach (ConfigurationElement elem in _values.ConfigurationElements) { if (elem.IsModified()) { return true; } } return false; } protected internal virtual void ResetModified() { _bModified = false; if (_lockedAttributesList != null) { _lockedAttributesList.ResetModified(); } if (_lockedAllExceptAttributesList != null) { _lockedAllExceptAttributesList.ResetModified(); } if (_lockedElementsList != null) { _lockedElementsList.ResetModified(); } if (_lockedAllExceptElementsList != null) { _lockedAllExceptElementsList.ResetModified(); } foreach (ConfigurationElement elem in _values.ConfigurationElements) { elem.ResetModified(); } } public virtual bool IsReadOnly() { return _bReadOnly; } protected internal virtual void SetReadOnly() { _bReadOnly = true; foreach (ConfigurationElement elem in _values.ConfigurationElements) { elem.SetReadOnly(); } } internal void SetLocked() { _fItemLocked = ConfigurationValueFlags.Locked | ConfigurationValueFlags.XMLParentInherited; foreach (ConfigurationProperty prop in Properties) { ConfigurationElement elem = this[prop] as ConfigurationElement; if (elem != null) { if (elem.GetType() != this.GetType()) { elem.SetLocked(); } ConfigurationElementCollection collection = this[prop] as ConfigurationElementCollection; if (collection != null) { foreach (object obj in collection) { ConfigurationElement element = obj as ConfigurationElement; if (element != null) { element.SetLocked(); } } } } } } // GetErrorsList // // Get the list of Errors for this location and all // sub locations // internal ArrayList GetErrorsList() { ArrayList errorList = new ArrayList(); ListErrors(errorList); return errorList; } // GetErrors // // Get a ConfigurationErrorsException that contains the errors // for this ConfigurationElement and its children // internal ConfigurationErrorsException GetErrors() { ArrayList errorsList; errorsList = GetErrorsList(); if (errorsList.Count == 0) { return null; } ConfigurationErrorsException e = new ConfigurationErrorsException(errorsList); return e; } protected virtual void ListErrors(IList errorList) { // First list errors in this element, then in subelements foreach (InvalidPropValue invalidValue in _values.InvalidValues) { errorList.Add(invalidValue.Error); } foreach (ConfigurationElement elem in _values.ConfigurationElements) { elem.ListErrors(errorList); ConfigurationElementCollection collection = elem as ConfigurationElementCollection; if (collection != null) { foreach (ConfigurationElement item in collection) { item.ListErrors(errorList); } } } } protected internal virtual void InitializeDefault() { } internal void CheckLockedElement(string elementName, XmlReader reader) { // have to check if clear was locked! if(elementName != null) { if(((_lockedElementsList != null) && (_lockedElementsList.DefinedInParent(LockAll) || _lockedElementsList.DefinedInParent(elementName))) || ((_lockedAllExceptElementsList != null && _lockedAllExceptElementsList.Count != 0) && _lockedAllExceptElementsList.HasParentElements && !_lockedAllExceptElementsList.DefinedInParent(elementName) || (_fItemLocked & ConfigurationValueFlags.Inherited) != 0) ) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_locked, elementName), reader); } } } internal void RemoveAllInheritedLocks() { if (_lockedAttributesList != null) { _lockedAttributesList.RemoveInheritedLocks(); } if (_lockedElementsList != null) { _lockedElementsList.RemoveInheritedLocks(); } if (_lockedAllExceptAttributesList != null) { _lockedAllExceptAttributesList.RemoveInheritedLocks(); } if (_lockedAllExceptElementsList != null) { _lockedAllExceptElementsList.RemoveInheritedLocks(); } } internal void ResetLockLists(ConfigurationElement parentElement) { _lockedAttributesList = null; _lockedAllExceptAttributesList = null; _lockedElementsList = null; _lockedAllExceptElementsList = null; if (parentElement != null) { _fItemLocked = ((parentElement._fItemLocked & ConfigurationValueFlags.Locked) != 0) ? (ConfigurationValueFlags.Inherited | parentElement._fItemLocked) : ConfigurationValueFlags.Default; if (parentElement._lockedAttributesList != null) { _lockedAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedAttributes); foreach (string key in parentElement._lockedAttributesList) _lockedAttributesList.Add(key, ConfigurationValueFlags.Inherited); // Mark entry as from the parent - read only } if (parentElement._lockedAllExceptAttributesList != null) { _lockedAllExceptAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedExceptionList, String.Empty, parentElement._lockedAllExceptAttributesList); } if (parentElement._lockedElementsList != null) { _lockedElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElements); ConfigurationElementCollection collection = null; if (Properties.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[Properties.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null) { collection.internalElementTagName = parentElement.ElementTagName; // Default collections don't know there tag name if (collection._lockedElementsList == null) { collection._lockedElementsList = _lockedElementsList; } } } foreach (string key in parentElement._lockedElementsList) { _lockedElementsList.Add(key, ConfigurationValueFlags.Inherited); // Mark entry as from the parent - read only } } if (parentElement._lockedAllExceptElementsList != null) { _lockedAllExceptElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElementsExceptionList, parentElement._elementTagName, parentElement._lockedAllExceptElementsList); ConfigurationElementCollection collection = null; if (Properties.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[Properties.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null && collection._lockedAllExceptElementsList == null) { collection._lockedAllExceptElementsList = _lockedAllExceptElementsList; } } } } } protected internal virtual void Reset(ConfigurationElement parentElement) { Values.Clear(); ResetLockLists(parentElement); ConfigurationPropertyCollection props = Properties; // Force the bag to be up to date _bElementPresent = false; if (parentElement == null) { InitializeDefault(); } else { bool hasAnyChildElements = false; ConfigurationPropertyCollection collectionKeys = null; for (int index = 0; index < parentElement.Values.Count; index++) { string key = parentElement.Values.GetKey(index); ConfigurationValue ConfigValue = parentElement.Values.GetConfigValue(index); object value = (ConfigValue != null) ? ConfigValue.Value : null; PropertySourceInfo sourceInfo = (ConfigValue != null) ? ConfigValue.SourceInfo : null; ConfigurationProperty prop = (ConfigurationProperty)parentElement.Properties[key]; if (prop == null || ((collectionKeys != null) && !collectionKeys.Contains(prop.Name))) { continue; } if (prop.IsConfigurationElementType) { hasAnyChildElements = true; } else { ConfigurationValueFlags flags = ConfigurationValueFlags.Inherited | (((_lockedAttributesList != null) && (_lockedAttributesList.Contains(key) || _lockedAttributesList.Contains(LockAll)) || (_lockedAllExceptAttributesList != null) && !_lockedAllExceptAttributesList.Contains(key)) ? ConfigurationValueFlags.Locked : ConfigurationValueFlags.Default); if (value != s_nullPropertyValue) { // _values[key] = value; _values.SetValue(key, value, flags, sourceInfo); } if (!props.Contains(key)) // this is for optional provider models keys { props.Add(prop); _values.SetValue(key, value, flags, sourceInfo); } } } if (hasAnyChildElements) { for (int index = 0; index < parentElement.Values.Count; index++) { string key = parentElement.Values.GetKey(index); object value = parentElement.Values[index]; ConfigurationProperty prop = (ConfigurationProperty)parentElement.Properties[key]; if ((prop != null) && prop.IsConfigurationElementType) { //((ConfigurationElement)value).SerializeToXmlElement(writer, prop.Name); ConfigurationElement childElement = (ConfigurationElement)this[prop]; childElement.Reset((ConfigurationElement)value); } } } } } public override bool Equals(object compareTo) { ConfigurationElement compareToElem = compareTo as ConfigurationElement; if (compareToElem == null || (compareTo.GetType() != this.GetType()) || ((compareToElem != null) && (compareToElem.Properties.Count != this.Properties.Count))) { return false; } foreach (ConfigurationProperty configProperty in this.Properties) { if (!Object.Equals(Values[configProperty.Name], compareToElem.Values[configProperty.Name])) { if (!(((Values[configProperty.Name] == null || Values[configProperty.Name] == s_nullPropertyValue) && Object.Equals(compareToElem.Values[configProperty.Name], configProperty.DefaultValue)) || ((compareToElem.Values[configProperty.Name] == null || compareToElem.Values[configProperty.Name] == s_nullPropertyValue) && Object.Equals(Values[configProperty.Name], configProperty.DefaultValue)))) return false; } } return true; } public override int GetHashCode() { int hHashCode = 0; foreach (ConfigurationProperty configProperty in this.Properties) { object o = this[configProperty]; if (o != null) { hHashCode ^= this[configProperty].GetHashCode(); } } return hHashCode; } protected internal Object this[ConfigurationProperty prop] { get { Object o = _values[prop.Name]; if (o == null) { if (prop.IsConfigurationElementType) { lock (_values.SyncRoot) { o = _values[prop.Name]; if (o == null) { ConfigurationElement childElement = CreateElement(prop.Type); if (_bReadOnly) { childElement.SetReadOnly(); } if (typeof(ConfigurationElementCollection).IsAssignableFrom(prop.Type)) { ConfigurationElementCollection childElementCollection = childElement as ConfigurationElementCollection; if (prop.AddElementName != null) childElementCollection.AddElementName = prop.AddElementName; if (prop.RemoveElementName != null) childElementCollection.RemoveElementName = prop.RemoveElementName; if (prop.ClearElementName != null) childElementCollection.ClearElementName = prop.ClearElementName; } //_values[prop.Name] = childElement; _values.SetValue(prop.Name, childElement, ConfigurationValueFlags.Inherited, null); o = childElement; } } } else { o = prop.DefaultValue; } } else if (o == s_nullPropertyValue) { o = null; } // If its an invalid value - throw the error now if (o is InvalidPropValue) { throw ((InvalidPropValue)o).Error; } return o; } set { SetPropertyValue(prop, value,false); // Do not ignore locks!!! } } protected internal Object this[String propertyName] { get { ConfigurationProperty prop = Properties[propertyName]; if (prop == null) { prop = Properties[DefaultCollectionPropertyName]; if (prop.ProvidedName != propertyName) { return null; } } return this[prop]; } set { Debug.Assert(Properties.Contains(propertyName), "Properties.Contains(propertyName)"); SetPropertyValue(Properties[propertyName], value, false);// Do not ignore locks!!! } } // Note: this method is completelly redundant ( the code is duplaicated in ConfigurationProperty( PropertyInfo ) ) // We do not remove the code now to minimize code changes for Whidbey RTM but this method and all calls leading to it should // be removed post-Whidbey private static void ApplyInstanceAttributes(object instance) { Debug.Assert(instance is ConfigurationElement, "instance is ConfigurationElement"); Type type = instance.GetType(); foreach (PropertyInfo propertyInformation in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { ConfigurationPropertyAttribute attribProperty = Attribute.GetCustomAttribute(propertyInformation, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute; if (attribProperty != null) { Type propertyType = propertyInformation.PropertyType; // Collections need some customization when the collection attribute is present if (typeof(ConfigurationElementCollection).IsAssignableFrom(propertyType)) { ConfigurationCollectionAttribute attribCollection = Attribute.GetCustomAttribute(propertyInformation, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; // If none on the property - see if there is an attribute on the collection type itself if (attribCollection == null) { attribCollection = Attribute.GetCustomAttribute(propertyType, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; } ConfigurationElementCollection coll = propertyInformation.GetValue(instance, null) as ConfigurationElementCollection; if (coll == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_element_null_instance, propertyInformation.Name, attribProperty.Name)); } // If the attribute is found - get the collection instance and set the data from the attribute if (attribCollection != null) { if (attribCollection.AddItemName.IndexOf(',') == -1) { coll.AddElementName = attribCollection.AddItemName; } coll.RemoveElementName = attribCollection.RemoveItemName; coll.ClearElementName = attribCollection.ClearItemsName; } } else if (typeof(ConfigurationElement).IsAssignableFrom(propertyType)) { // Nested configuration element - handle recursively object element = propertyInformation.GetValue(instance, null); if (element == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_element_null_instance, propertyInformation.Name,attribProperty.Name)); } ApplyInstanceAttributes(element); } } } } private static bool PropertiesFromType(Type type, out ConfigurationPropertyCollection result) { ConfigurationPropertyCollection properties = (ConfigurationPropertyCollection)s_propertyBags[type]; result = null; bool firstTimeInit = false; if (properties == null) { lock (s_propertyBags.SyncRoot) { properties = (ConfigurationPropertyCollection)s_propertyBags[type]; if (properties == null) { properties = CreatePropertyBagFromType(type); s_propertyBags[type] = properties; firstTimeInit = true; } } } result = properties; return firstTimeInit; } private static ConfigurationPropertyCollection CreatePropertyBagFromType(Type type) { Debug.Assert(type != null, "type != null"); // For ConfigurationElement derived classes - get the per-type validator if (typeof(ConfigurationElement).IsAssignableFrom(type)) { ConfigurationValidatorAttribute attribValidator = Attribute.GetCustomAttribute(type, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute; if (attribValidator != null) { attribValidator.SetDeclaringType(type); ConfigurationValidatorBase validator = attribValidator.ValidatorInstance; if (validator != null) { CachePerTypeValidator(type, validator); } } } ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection(); foreach (PropertyInfo propertyInformation in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { ConfigurationProperty newProp = CreateConfigurationPropertyFromAttributes(propertyInformation); if (newProp != null) { properties.Add(newProp); } } return properties; } private static ConfigurationProperty CreateConfigurationPropertyFromAttributes(PropertyInfo propertyInformation) { Debug.Assert(propertyInformation != null, "propertyInformation != null"); ConfigurationProperty result = null; ConfigurationPropertyAttribute attribProperty = Attribute.GetCustomAttribute(propertyInformation, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute; // If there is no ConfigurationProperty attrib - this is not considered a property if (attribProperty != null) { result = new ConfigurationProperty(propertyInformation); } // Handle some special cases of property types if (result != null && typeof(ConfigurationElement).IsAssignableFrom(result.Type)) { ConfigurationPropertyCollection unused = null; PropertiesFromType(result.Type, out unused); } return result; } private static void CachePerTypeValidator( Type type, ConfigurationValidatorBase validator ) { Debug.Assert((type != null) && ( validator != null)); Debug.Assert(typeof(ConfigurationElement).IsAssignableFrom(type)); // Use the same lock as the property bag lock since in the current implementation // the only way to get to this method is through the code path that locks the property bag cache first ( see PropertiesFromType() ) // NOTE[ Thread Safety ]: Non-guarded access to static variable - since this code is called only from CreatePropertyBagFromType // which in turn is done onle once per type and is guarded by the s_propertyBag.SyncRoot then this call is thread safe as well if (s_perTypeValidators == null ) { s_perTypeValidators = new Dictionary(); } // A type validator should be cached only once. If it isn't then attribute parsing is done more then once which should be avoided Debug.Assert( !s_perTypeValidators.ContainsKey(type)); // Make sure the supplied validator supports validating this object if (!validator.CanValidate(type)) { throw new ConfigurationErrorsException(SR.GetString(SR.Validator_does_not_support_elem_type, type.Name)); } s_perTypeValidators.Add(type, validator); } private static void ApplyValidatorsRecursive(ConfigurationElement root) { Debug.Assert(root != null); // Apply the validator on 'root' ApplyValidator(root); // Apply validators on child elements ( note - we will do this only on already created child elements // The non created ones will get their validators in the ctor foreach (ConfigurationElement elem in root._values.ConfigurationElements) { ApplyValidatorsRecursive(elem); } } private static void ApplyValidator(ConfigurationElement elem) { Debug.Assert(elem != null); if ((s_perTypeValidators != null) && (s_perTypeValidators.ContainsKey(elem.GetType()))) { elem._elementProperty = new ConfigurationElementProperty(s_perTypeValidators[ elem.GetType() ]); } } protected void SetPropertyValue(ConfigurationProperty prop, object value, bool ignoreLocks) { if (IsReadOnly()) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only)); } if ((ignoreLocks == false) && ((_lockedAllExceptAttributesList != null && _lockedAllExceptAttributesList.HasParentElements && !_lockedAllExceptAttributesList.DefinedInParent(prop.Name)) || (_lockedAttributesList != null && (_lockedAttributesList.DefinedInParent(prop.Name) || _lockedAttributesList.DefinedInParent(LockAll))) || ((_fItemLocked & ConfigurationValueFlags.Locked) != 0) && (_fItemLocked & ConfigurationValueFlags.Inherited) != 0)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, prop.Name)); } _bModified = true; // Run the new value through the validator to make sure its ok to store it if (value != null) { prop.Validate(value); } _values[prop.Name] = (value != null) ? value : s_nullPropertyValue; } protected internal virtual ConfigurationPropertyCollection Properties { get { ConfigurationPropertyCollection result = null; if (PropertiesFromType(this.GetType(), out result)) { ApplyInstanceAttributes(this); // Redundant but preserved to minimize code changes for Whidbey RTM ApplyValidatorsRecursive(this); } return result; } } internal ConfigurationValues Values { get { return _values; } } internal PropertySourceInfo PropertyInfoInternal(string propertyName) { return (PropertySourceInfo)_values.GetSourceInfo(propertyName); } internal string PropertyFileName(string propertyName) { PropertySourceInfo p = (PropertySourceInfo)PropertyInfoInternal(propertyName); if (p == null) p = (PropertySourceInfo)PropertyInfoInternal(String.Empty); // Get the filename of the parent if prop is not there if (p == null) return String.Empty; return p.FileName; } internal int PropertyLineNumber(string propertyName) { PropertySourceInfo p = (PropertySourceInfo)PropertyInfoInternal(propertyName); if (p == null) p = (PropertySourceInfo)PropertyInfoInternal(String.Empty); if (p == null) return 0; return p.LineNumber; } internal virtual void Dump(TextWriter tw) { tw.WriteLine("Type: " + GetType().FullName); foreach (PropertyInfo pi in GetType().GetProperties()) { tw.WriteLine("{0}: {1}", pi.Name, pi.GetValue(this, null)); } } protected internal virtual void Unmerge(ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode) { if (sourceElement != null) { bool hasAnyChildElements = false; _lockedAllExceptAttributesList = sourceElement._lockedAllExceptAttributesList; _lockedAllExceptElementsList = sourceElement._lockedAllExceptElementsList; _fItemLocked = sourceElement._fItemLocked; _lockedAttributesList = sourceElement._lockedAttributesList; _lockedElementsList = sourceElement._lockedElementsList; AssociateContext(sourceElement._configRecord); if (parentElement != null) { if (parentElement._lockedAttributesList != null) _lockedAttributesList = UnMergeLockList(sourceElement._lockedAttributesList, parentElement._lockedAttributesList, saveMode); if (parentElement._lockedElementsList != null) _lockedElementsList = UnMergeLockList(sourceElement._lockedElementsList, parentElement._lockedElementsList, saveMode); if (parentElement._lockedAllExceptAttributesList != null) _lockedAllExceptAttributesList = UnMergeLockList(sourceElement._lockedAllExceptAttributesList, parentElement._lockedAllExceptAttributesList, saveMode); if (parentElement._lockedAllExceptElementsList != null) _lockedAllExceptElementsList = UnMergeLockList(sourceElement._lockedAllExceptElementsList, parentElement._lockedAllExceptElementsList, saveMode); } ConfigurationPropertyCollection props = Properties; ConfigurationPropertyCollection collectionKeys = null; // check for props not in bag from source for (int index = 0; index < sourceElement.Values.Count; index++) { string key = sourceElement.Values.GetKey(index); object value = sourceElement.Values[index]; ConfigurationProperty prop = (ConfigurationProperty)sourceElement.Properties[key]; if (prop == null || (collectionKeys != null && !collectionKeys.Contains(prop.Name))) continue; if (prop.IsConfigurationElementType) { hasAnyChildElements = true; } else { if (value != s_nullPropertyValue) { if (!props.Contains(key)) // this is for optional provider models keys { // _values[key] = value; ConfigurationValueFlags valueFlags = sourceElement.Values.RetrieveFlags(key); _values.SetValue(key, value, valueFlags, null); props.Add(prop); } } } } foreach (ConfigurationProperty prop in Properties) { if (prop == null || (collectionKeys != null && !collectionKeys.Contains(prop.Name))) { continue; } if (prop.IsConfigurationElementType) { hasAnyChildElements = true; } else { object value = sourceElement.Values[prop.Name]; // if the property is required or we are writing a full config make sure we have defaults if ((prop.IsRequired == true || saveMode == ConfigurationSaveMode.Full) && (value == null || value == s_nullPropertyValue)) { // If the default value is null, this means there wasnt a reasonable default for the value // and there is nothing more we can do. Otherwise reset the value to the default // Note: 'null' should be used as default for non-empty strings instead // of the current practice to use String.Epmty if (prop.DefaultValue != null) { value = prop.DefaultValue; // need to make sure required properties are persisted } } if (value != null && value != s_nullPropertyValue) { object value2 = null; if (parentElement != null) // Is there a parent value2 = parentElement.Values[prop.Name]; // if so get it's value if (value2 == null) // no parent use default value2 = prop.DefaultValue; // If changed and not same as parent write or required switch (saveMode) { case ConfigurationSaveMode.Minimal: { if (!Object.Equals(value, value2) || prop.IsRequired == true) _values[prop.Name] = value; } break; // (value != null && value != s_nullPropertyValue) || case ConfigurationSaveMode.Modified: { bool modified = sourceElement.Values.IsModified(prop.Name); bool inherited = sourceElement.Values.IsInherited(prop.Name); // update the value if the property is required, modified or it was not inherited // Also update properties that ARE inherited when we are resetting the object // as long as the property is not the same as the default value for the property if ((prop.IsRequired || modified || !inherited) || (parentElement == null && inherited && !Object.Equals(value, value2))) { _values[prop.Name] = value; } } break; case ConfigurationSaveMode.Full: { if (value != null && value != s_nullPropertyValue) _values[prop.Name] = value; else _values[prop.Name] = value2; } break; } } } } if (hasAnyChildElements) { foreach (ConfigurationProperty prop in Properties) { if (prop.IsConfigurationElementType) { ConfigurationElement pElem = (ConfigurationElement)((parentElement != null) ? parentElement[prop] : null); ConfigurationElement childElement = (ConfigurationElement)this[prop]; if ((ConfigurationElement)sourceElement[prop] != null) childElement.Unmerge((ConfigurationElement)sourceElement[prop], pElem, saveMode); } } } } } protected internal virtual bool SerializeToXmlElement(XmlWriter writer, String elementName) { if (_configRecord != null && _configRecord.TargetFramework != null) { ConfigurationSection section = null; if (_configRecord.SectionsStack.Count >0) { section = _configRecord.SectionsStack.Peek() as ConfigurationSection; } if (section != null && !section.ShouldSerializeElementInTargetVersion(this, elementName, _configRecord.TargetFramework)) { return false; } } bool DataToWrite = _bDataToWrite; // Don't write elements that are locked in the parent if ((_lockedElementsList != null && _lockedElementsList.DefinedInParent(elementName)) || (_lockedAllExceptElementsList != null && _lockedAllExceptElementsList.HasParentElements && !_lockedAllExceptElementsList.DefinedInParent(elementName))) { return DataToWrite; } if (SerializeElement(null, false) == true) // check if there is anything to write... { if (writer != null) writer.WriteStartElement(elementName); DataToWrite |= SerializeElement(writer, false); if (writer != null) writer.WriteEndElement(); } return DataToWrite; } protected internal virtual bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) { PreSerialize(writer); bool DataToWrite = _bDataToWrite; bool hasAnyChildElements = false; bool foundDefaultElement = false; ConfigurationPropertyCollection props = Properties; ConfigurationPropertyCollection collectionKeys = null; for (int index = 0; index < _values.Count; index++) { string key = _values.GetKey(index); object value = _values[index]; ConfigurationProperty prop = (ConfigurationProperty)props[key]; if (prop == null || (collectionKeys != null && !collectionKeys.Contains(prop.Name))) { continue; } if ( prop.IsVersionCheckRequired && _configRecord != null && _configRecord.TargetFramework != null) { ConfigurationSection section = null; if (_configRecord.SectionsStack.Count >0) { section = _configRecord.SectionsStack.Peek() as ConfigurationSection; } if (section != null && !section.ShouldSerializePropertyInTargetVersion(prop, prop.Name, _configRecord.TargetFramework, this)) { continue; } } if (prop.IsConfigurationElementType) { hasAnyChildElements = true; } else { if ((_lockedAllExceptAttributesList != null && _lockedAllExceptAttributesList.HasParentElements && !_lockedAllExceptAttributesList.DefinedInParent(prop.Name)) || (_lockedAttributesList != null && _lockedAttributesList.DefinedInParent(prop.Name))) { if (prop.IsRequired == true) throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_required_attribute_locked, prop.Name)); value = s_nullPropertyValue; } if (value != s_nullPropertyValue) { if (serializeCollectionKey == false || prop.IsKey == true) { string xmlValue = null; // If this was an invalid string value and was cached - write it out as is if (value is InvalidPropValue) { xmlValue = ((InvalidPropValue)value).Value; } else { prop.Validate(value); xmlValue = prop.ConvertToString(value); } if ((xmlValue != null) && (writer != null)) { if (prop.IsTypeStringTransformationRequired) xmlValue = GetTransformedTypeString(xmlValue); if (prop.IsAssemblyStringTransformationRequired) xmlValue = GetTransformedAssemblyString(xmlValue); writer.WriteAttributeString(prop.Name, xmlValue); } DataToWrite = DataToWrite || (xmlValue != null); } } } } if (serializeCollectionKey == false) { DataToWrite |= SerializeLockList(_lockedAttributesList, LockAttributesKey, writer); DataToWrite |= SerializeLockList(_lockedAllExceptAttributesList, LockAllAttributesExceptKey, writer); DataToWrite |= SerializeLockList(_lockedElementsList, LockElementsKey, writer); DataToWrite |= SerializeLockList(_lockedAllExceptElementsList, LockAllElementsExceptKey, writer); if ((_fItemLocked & ConfigurationValueFlags.Locked) != 0 && (_fItemLocked & ConfigurationValueFlags.Inherited) == 0 && (_fItemLocked & ConfigurationValueFlags.XMLParentInherited) == 0) { DataToWrite = true; if (writer != null) writer.WriteAttributeString(LockItemKey, true.ToString().ToLower(CultureInfo.InvariantCulture)); } } if (hasAnyChildElements) { for (int index = 0; index < _values.Count; index++) { string key = _values.GetKey(index); object value = _values[index]; ConfigurationProperty prop = (ConfigurationProperty)props[key]; // if we are writing a remove and the sub element is not part of the key don't write it. if (serializeCollectionKey == false || prop.IsKey == true) { if (value is ConfigurationElement) { if (!((_lockedElementsList != null && _lockedElementsList.DefinedInParent(key)) || (_lockedAllExceptElementsList != null && _lockedAllExceptElementsList.HasParentElements && !_lockedAllExceptElementsList.DefinedInParent(key)))) { ConfigurationElement elem = (ConfigurationElement)value; if (prop.Name != ConfigurationProperty.DefaultCollectionPropertyName) { DataToWrite |= elem.SerializeToXmlElement(writer, prop.Name); } else if (!foundDefaultElement) { // Prevent the locks from serializing a second time since locks // on a default collection serialize with their parent node elem._lockedAttributesList = null; elem._lockedAllExceptAttributesList = null; elem._lockedElementsList = null; elem._lockedAllExceptElementsList = null; DataToWrite |= elem.SerializeElement(writer, false); foundDefaultElement = true; } else { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_cannot_have_multiple_child_elements, prop.Name)); } } } } } } return DataToWrite; } private bool SerializeLockList(ConfigurationLockCollection list, String elementKey, XmlWriter writer) { StringBuilder sb; sb = new StringBuilder(); if (list != null) { foreach (string key in list) { if (!list.DefinedInParent(key)) { if (sb.Length != 0) sb.Append(','); sb.Append((string)key); } } } if (writer != null && sb.Length != 0) writer.WriteAttributeString(elementKey, sb.ToString()); return (sb.Length != 0); } internal void ReportInvalidLock(string attribToLockTrim, ConfigurationLockCollectionType lockedType, ConfigurationValue value, String collectionProperties) { StringBuilder sb; sb = new StringBuilder(); // Add the collection properties when locking elements if (!String.IsNullOrEmpty(collectionProperties) && ((lockedType == ConfigurationLockCollectionType.LockedElements) || (lockedType == ConfigurationLockCollectionType.LockedElementsExceptionList))) { if (sb.Length != 0) sb.Append(','); sb.Append(collectionProperties); } // construct a list of valid lockable properties foreach (object _prop in Properties) { ConfigurationProperty validProp = (ConfigurationProperty)_prop; if (validProp.Name != LockAttributesKey && validProp.Name != LockAllAttributesExceptKey && validProp.Name != LockElementsKey && validProp.Name != LockAllElementsExceptKey ) { if ((lockedType == ConfigurationLockCollectionType.LockedElements) || (lockedType == ConfigurationLockCollectionType.LockedElementsExceptionList)) { if (typeof(ConfigurationElement).IsAssignableFrom(validProp.Type)) { if (sb.Length != 0) sb.Append(", "); sb.Append("'"); sb.Append(validProp.Name); sb.Append("'"); } } else { if (!typeof(ConfigurationElement).IsAssignableFrom(validProp.Type)) { if (sb.Length != 0) sb.Append(", "); sb.Append("'"); sb.Append(validProp.Name); sb.Append("'"); } } } } string format = null; if ((lockedType == ConfigurationLockCollectionType.LockedElements) || (lockedType == ConfigurationLockCollectionType.LockedElementsExceptionList)) { if (value != null) format = SR.GetString(SR.Config_base_invalid_element_to_lock); else format = SR.GetString(SR.Config_base_invalid_element_to_lock_by_add); } else { if (value != null) format = SR.GetString(SR.Config_base_invalid_attribute_to_lock); else format = SR.GetString(SR.Config_base_invalid_attribute_to_lock_by_add); } if (value != null) throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, format, attribToLockTrim, sb.ToString()), value.SourceInfo.FileName, value.SourceInfo.LineNumber); else throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, format, attribToLockTrim, sb.ToString())); } private ConfigurationLockCollection ParseLockedAttributes(ConfigurationValue value, ConfigurationLockCollectionType lockType) { // check that only actual properties are in the lock attribute ConfigurationLockCollection localLockedAttributesList = new ConfigurationLockCollection(this, lockType); string attributeList = (string)(value.Value); if (string.IsNullOrEmpty(attributeList)) { if (lockType == ConfigurationLockCollectionType.LockedAttributes) throw new ConfigurationErrorsException(SR.GetString(SR.Empty_attribute, LockAttributesKey), value.SourceInfo.FileName, value.SourceInfo.LineNumber); if (lockType == ConfigurationLockCollectionType.LockedElements) throw new ConfigurationErrorsException(SR.GetString(SR.Empty_attribute, LockElementsKey), value.SourceInfo.FileName, value.SourceInfo.LineNumber); if (lockType == ConfigurationLockCollectionType.LockedExceptionList) throw new ConfigurationErrorsException(SR.GetString(SR.Config_empty_lock_attributes_except, LockAllAttributesExceptKey, LockAttributesKey), value.SourceInfo.FileName, value.SourceInfo.LineNumber); if (lockType == ConfigurationLockCollectionType.LockedElementsExceptionList) throw new ConfigurationErrorsException(SR.GetString(SR.Config_empty_lock_element_except, LockAllElementsExceptKey, LockElementsKey), value.SourceInfo.FileName, value.SourceInfo.LineNumber); } string[] attribsToLock = attributeList.Split(new char[] { ',', ':', ';' }); foreach (string attribToLock in attribsToLock) { string attribToLockTrim = attribToLock.Trim(); if (!String.IsNullOrEmpty(attribToLockTrim)) { // validate that the locks are good if (!((lockType == ConfigurationLockCollectionType.LockedElements || lockType == ConfigurationLockCollectionType.LockedAttributes) && attribToLockTrim == LockAll)) { ConfigurationProperty propToLock = Properties[attribToLockTrim]; if (propToLock == null || // if the prop does not exist attribToLockTrim == LockAttributesKey || // or it is the lockattributes keyword attribToLockTrim == LockAllAttributesExceptKey || // or it is the lockattributes keyword attribToLockTrim == LockElementsKey || // or it is the lockelements keyword (lockType != ConfigurationLockCollectionType.LockedElements && lockType != ConfigurationLockCollectionType.LockedElementsExceptionList && typeof(ConfigurationElement).IsAssignableFrom(propToLock.Type)) || // or if not locking elements but the property is a element ((lockType == ConfigurationLockCollectionType.LockedElements || lockType == ConfigurationLockCollectionType.LockedElementsExceptionList) && !typeof(ConfigurationElement).IsAssignableFrom(propToLock.Type)) // or if locking elements but the property is not an element ) { // check to see if this is a collection and we are locking a collection element ConfigurationElementCollection collection = this as ConfigurationElementCollection; if (collection == null && Properties.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[Properties.DefaultCollectionProperty] as ConfigurationElementCollection; } if (collection == null || lockType == ConfigurationLockCollectionType.LockedAttributes || // If the collection type is not element then the lock is bogus lockType == ConfigurationLockCollectionType.LockedExceptionList) { ReportInvalidLock(attribToLockTrim, lockType, value, null); } else if (!collection.IsLockableElement(attribToLockTrim)) { ReportInvalidLock(attribToLockTrim, lockType, value, collection.LockableElements); } } if (propToLock != null && propToLock.IsRequired == true) throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_required_attribute_lock_attempt, propToLock.Name)); } // concatenate the new attribute. localLockedAttributesList.Add(attribToLockTrim, ConfigurationValueFlags.Default); // Mark as local } } return localLockedAttributesList; } private StringCollection IntersectLockCollections(ConfigurationLockCollection Collection1, ConfigurationLockCollection Collection2) { ConfigurationLockCollection smallCollection = Collection1.Count < Collection2.Count ? Collection1 : Collection2; ConfigurationLockCollection largeCollection = Collection1.Count >= Collection2.Count ? Collection1 : Collection2; StringCollection intersectionCollection = new StringCollection(); foreach (string key in smallCollection) { if (largeCollection.Contains(key) || key == ElementTagName) intersectionCollection.Add(key); // add the local copy } return intersectionCollection; } protected internal virtual void DeserializeElement(XmlReader reader, bool serializeCollectionKey) { ConfigurationPropertyCollection props = Properties; ConfigurationValue LockedAttributesList = null; ConfigurationValue LockedAllExceptList = null; ConfigurationValue LockedElementList = null; ConfigurationValue LockedAllElementsExceptList = null; bool ItemLockedLocally = false; _bElementPresent = true; ConfigurationElement defaultCollection = null; ConfigurationProperty defaultCollectionProperty = props != null ? props.DefaultCollectionProperty : null; if (defaultCollectionProperty != null) { defaultCollection = (ConfigurationElement)this[defaultCollectionProperty]; } // Process attributes _elementTagName = reader.Name; PropertySourceInfo rootInfo = new PropertySourceInfo(reader); _values.SetValue(reader.Name, null, ConfigurationValueFlags.Modified, rootInfo); _values.SetValue(DefaultCollectionPropertyName, defaultCollection, ConfigurationValueFlags.Modified, rootInfo); if ((_lockedElementsList != null && (_lockedElementsList.Contains(reader.Name) || (_lockedElementsList.Contains(LockAll) && reader.Name != ElementTagName))) || (_lockedAllExceptElementsList != null && _lockedAllExceptElementsList.Count != 0 && !_lockedAllExceptElementsList.Contains(reader.Name)) || ((_fItemLocked & ConfigurationValueFlags.Locked) != 0 && (_fItemLocked & ConfigurationValueFlags.Inherited) != 0) ) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_locked, reader.Name), reader); } if (reader.AttributeCount > 0) { while (reader.MoveToNextAttribute()) { String propertyName = reader.Name; if ((_lockedAttributesList != null && (_lockedAttributesList.Contains(propertyName) || _lockedAttributesList.Contains(LockAll))) || (_lockedAllExceptAttributesList != null && !_lockedAllExceptAttributesList.Contains(propertyName)) ) { if (propertyName != LockAttributesKey && propertyName != LockAllAttributesExceptKey) throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, propertyName), reader); } ConfigurationProperty prop = props != null ? props[propertyName] : null; if (prop != null) { if (serializeCollectionKey && !prop.IsKey) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_unrecognized_attribute, propertyName), reader); } _values.SetValue(propertyName, DeserializePropertyValue(prop, reader), ConfigurationValueFlags.Modified, new PropertySourceInfo(reader)); } // if (deserializing a remove OR an add that does not handle optional attributes) else if (propertyName == LockItemKey) { try { ItemLockedLocally = bool.Parse(reader.Value); } catch { throw new ConfigurationErrorsException(SR.GetString(SR.Config_invalid_boolean_attribute, propertyName), reader); } } else if (propertyName == LockAttributesKey) { LockedAttributesList = new ConfigurationValue(reader.Value, ConfigurationValueFlags.Default, new PropertySourceInfo(reader)); } else if (propertyName == LockAllAttributesExceptKey) { LockedAllExceptList = new ConfigurationValue(reader.Value, ConfigurationValueFlags.Default, new PropertySourceInfo(reader)); } else if (propertyName == LockElementsKey) { LockedElementList = new ConfigurationValue(reader.Value, ConfigurationValueFlags.Default, new PropertySourceInfo(reader)); } else if (propertyName == LockAllElementsExceptKey) { LockedAllElementsExceptList = new ConfigurationValue(reader.Value, ConfigurationValueFlags.Default, new PropertySourceInfo(reader)); } else if (serializeCollectionKey || !OnDeserializeUnrecognizedAttribute(propertyName, reader.Value)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_unrecognized_attribute, propertyName), reader); } } } reader.MoveToElement(); // Check for nested elements. try { HybridDictionary nodeFound = new HybridDictionary(); if (!reader.IsEmptyElement) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { String propertyName = reader.Name; CheckLockedElement(propertyName, null); ConfigurationProperty prop = props != null ? props[propertyName] : null; if (prop != null) { if (prop.IsConfigurationElementType) { // if (nodeFound.Contains(propertyName)) throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_cannot_have_multiple_child_elements, propertyName), reader); nodeFound.Add(propertyName, propertyName); ConfigurationElement childElement = (ConfigurationElement)this[prop]; childElement.DeserializeElement(reader, serializeCollectionKey); // Validate the new element with the per-property Validator // Note that the per-type validator for childElement has been already executed as part of Deserialize ValidateElement(childElement, prop.Validator, false); } else { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_property_is_not_a_configuration_element, propertyName), reader); } } else if (!OnDeserializeUnrecognizedElement(propertyName, reader)) { // Let the default collection, if there is one, handle this node. if (defaultCollection == null || !defaultCollection.OnDeserializeUnrecognizedElement(propertyName, reader)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_unrecognized_element_name, propertyName), reader); } } } else if (reader.NodeType == XmlNodeType.EndElement) { break; } else if ((reader.NodeType == XmlNodeType.CDATA) || (reader.NodeType == XmlNodeType.Text)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_section_invalid_content), reader); } } } EnsureRequiredProperties(serializeCollectionKey); // Call the per-type validator for this object ValidateElement(this, null, false); } catch (ConfigurationException e) { // Catch the generic message from deserialization and include line info if necessary if (e.Filename == null || e.Filename.Length == 0) throw new ConfigurationErrorsException(e.Message, reader); // give it some info else throw e; } if (ItemLockedLocally) { SetLocked(); _fItemLocked = ConfigurationValueFlags.Locked; } if (LockedAttributesList != null) { if (_lockedAttributesList == null) _lockedAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedAttributes); foreach (string key in ParseLockedAttributes(LockedAttributesList, ConfigurationLockCollectionType.LockedAttributes)) { if (!_lockedAttributesList.Contains(key)) _lockedAttributesList.Add(key, ConfigurationValueFlags.Default); // add the local copy else _lockedAttributesList.Add(key, ConfigurationValueFlags.Modified | ConfigurationValueFlags.Inherited); // add the local copy } } if (LockedAllExceptList != null) { ConfigurationLockCollection newCollection = ParseLockedAttributes(LockedAllExceptList, ConfigurationLockCollectionType.LockedExceptionList); if (_lockedAllExceptAttributesList == null) { _lockedAllExceptAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedExceptionList, String.Empty, newCollection); _lockedAllExceptAttributesList.ClearSeedList(); // Prevent the list from thinking this was set by a parent. } StringCollection intersectionCollection = IntersectLockCollections(_lockedAllExceptAttributesList, newCollection); /* if (intersectionCollection.Count == 0) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_empty_lock_attributes_except_effective, LockAllAttributesExceptKey, LockedAllExceptList.Value, LockAttributesKey), LockedAllExceptList.SourceInfo.FileName, LockedAllExceptList.SourceInfo.LineNumber); } */ _lockedAllExceptAttributesList.ClearInternal(false); foreach (string key in intersectionCollection) { _lockedAllExceptAttributesList.Add(key, ConfigurationValueFlags.Default); } } if (LockedElementList != null) { if (_lockedElementsList == null) _lockedElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElements); ConfigurationLockCollection localLockedElementList = ParseLockedAttributes(LockedElementList, ConfigurationLockCollectionType.LockedElements); ConfigurationElementCollection collection = null; if (props.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[props.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null && collection._lockedElementsList == null) collection._lockedElementsList = _lockedElementsList; } foreach (string key in localLockedElementList) { if (!_lockedElementsList.Contains(key)) { _lockedElementsList.Add(key, ConfigurationValueFlags.Default); // add the local copy ConfigurationProperty propToLock = Properties[key]; if (propToLock != null && typeof(ConfigurationElement).IsAssignableFrom(propToLock.Type)) { ((ConfigurationElement)this[key]).SetLocked(); } if (key == LockAll) { foreach (ConfigurationProperty prop in Properties) { if (!string.IsNullOrEmpty(prop.Name) && prop.IsConfigurationElementType) { ((ConfigurationElement)this[prop]).SetLocked(); } } } } } } if (LockedAllElementsExceptList != null) { ConfigurationLockCollection newCollection = ParseLockedAttributes(LockedAllElementsExceptList, ConfigurationLockCollectionType.LockedElementsExceptionList); if (_lockedAllExceptElementsList == null) { _lockedAllExceptElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElementsExceptionList, _elementTagName, newCollection); _lockedAllExceptElementsList.ClearSeedList(); } StringCollection intersectionCollection = IntersectLockCollections(_lockedAllExceptElementsList, newCollection); ConfigurationElementCollection collection = null; if (props.DefaultCollectionProperty != null) // this is not a collection but it may contain a default collection { collection = this[props.DefaultCollectionProperty] as ConfigurationElementCollection; if (collection != null && collection._lockedAllExceptElementsList == null) collection._lockedAllExceptElementsList = _lockedAllExceptElementsList; } _lockedAllExceptElementsList.ClearInternal(false); foreach (string key in intersectionCollection) { if (!_lockedAllExceptElementsList.Contains(key) || key == ElementTagName) _lockedAllExceptElementsList.Add(key, ConfigurationValueFlags.Default); // add the local copy } foreach (ConfigurationProperty prop in Properties) { if (!(string.IsNullOrEmpty(prop.Name) || _lockedAllExceptElementsList.Contains(prop.Name)) && prop.IsConfigurationElementType) { ((ConfigurationElement)this[prop]).SetLocked(); } } } // Make sure default collections use the same lock element lists if (defaultCollectionProperty != null) { defaultCollection = (ConfigurationElement)this[defaultCollectionProperty]; if (_lockedElementsList == null) { _lockedElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElements); } defaultCollection._lockedElementsList = _lockedElementsList; if (_lockedAllExceptElementsList == null) { _lockedAllExceptElementsList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedElementsExceptionList, reader.Name); _lockedAllExceptElementsList.ClearSeedList(); } defaultCollection._lockedAllExceptElementsList = _lockedAllExceptElementsList; } // This has to be the last thing to execute PostDeserialize(); } private object DeserializePropertyValue(ConfigurationProperty prop, XmlReader reader) { Debug.Assert(prop != null, "prop != null"); Debug.Assert(reader != null, "reader != null"); // By default we try to load (i.e. parse/validate ) all properties // If a property value is invalid ( cannot be parsed or is not valid ) we will keep the value // as string ( from the xml ) and will write it out unchanged if needed // If the property value is needed by users the actuall exception will be thrown string xmlValue = reader.Value; object propertyValue = null; try { propertyValue = prop.ConvertFromString(xmlValue); // Validate the loaded and converted value prop.Validate(propertyValue); } catch (ConfigurationException ce) { // If the error is incomplete - complete it :) if (string.IsNullOrEmpty(ce.Filename)) { ce = new ConfigurationErrorsException(ce.Message, reader); } // Cannot parse/validate the value. Keep it as string propertyValue = new InvalidPropValue(xmlValue, ce); } catch { // If this is an exception related to the parsing/validating the // value ConfigurationErrorsException should be thrown instead. // If not - the exception is ok to surface out of here Debug.Fail("Unknown exception type thrown"); } return propertyValue; } internal static void ValidateElement(ConfigurationElement elem, ConfigurationValidatorBase propValidator, bool recursive) { // Validate a config element with the per-type validator when a per-property ( propValidator ) is not supplied // or with the per-prop validator when the element ( elem ) is a child property of another configuration element ConfigurationValidatorBase validator = propValidator; if ((validator == null) && // Not a property - use the per-type validator (elem.ElementProperty != null)) { validator = elem.ElementProperty.Validator; // Since ElementProperty can be overriden by derived classes we need to make sure that // the validator supports the type of elem every time if ((validator != null) && !validator.CanValidate(elem.GetType())) { throw new ConfigurationErrorsException(SR.GetString(SR.Validator_does_not_support_elem_type, elem.GetType().Name)); } } try { if (validator != null) { validator.Validate(elem); } } catch (ConfigurationException) { // ConfigurationElement validators are allowed to throw ConfigurationErrorsException. throw; } catch (Exception ex) { throw new ConfigurationErrorsException(SR.GetString(SR.Validator_element_not_valid, elem._elementTagName, ex.Message)); } if (recursive == true) { // Validate collection items: // Note: this is a bit of a hack - we will exploit the fact that this method is called with recursive == true only when serializing the top level section // At deserializtion time the per-element validator for collection items will get executed as part of their deserialization logic // However we dont perform validation in the serialization logic ( because at that time the object is unmerged and not all data is present ) // so we have to do that validation here. if (elem is ConfigurationElementCollection) { if (elem is ConfigurationElementCollection) { IEnumerator it = ((ConfigurationElementCollection)elem).GetElementsEnumerator(); while( it.MoveNext() ) { ValidateElement((ConfigurationElement)it.Current, null, true); } } } // Validate all child elements recursively for (int index = 0; index < elem.Values.Count; index++) { ConfigurationElement value = elem.Values[index] as ConfigurationElement; if (value != null) { // Run the per-type validator on the child element and proceed with validation in subelements // Note we dont run the per-property validator here since we run those when the property value is set ValidateElement(value, null, true); // per-type } } } } private void EnsureRequiredProperties(bool ensureKeysOnly) { ConfigurationPropertyCollection props = Properties; // Make sure all required properties are here if (props != null) { foreach (ConfigurationProperty prop in props) { // The property is required but no value was found if (prop.IsRequired && !_values.Contains(prop.Name)) { // Required properties can be ommited when we need only the keys to be there if (!ensureKeysOnly || prop.IsKey) { _values[prop.Name] = OnRequiredPropertyNotFound(prop.Name); } } } } } protected virtual object OnRequiredPropertyNotFound(string name) { // Derivied classes can override this to return a value for a required property that is missing // Here we treat this as an error though throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_required_attribute_missing, name), PropertyFileName(name), PropertyLineNumber(name)); } protected virtual void PostDeserialize() { // Please try to not add code in here } protected virtual void PreSerialize(XmlWriter writer) { // Please try to not add code in here } protected virtual bool OnDeserializeUnrecognizedAttribute(String name, String value) { return false; } protected virtual bool OnDeserializeUnrecognizedElement(String elementName, XmlReader reader) { return false; } protected virtual string GetTransformedTypeString(string typeName) { if ( typeName == null || _configRecord == null || !_configRecord.TypeStringTransformerIsSet) return typeName; else return _configRecord.TypeStringTransformer(typeName); } protected virtual string GetTransformedAssemblyString(string assemblyName) { if ( assemblyName == null || _configRecord == null || !_configRecord.AssemblyStringTransformerIsSet) return assemblyName; else return _configRecord.AssemblyStringTransformer(assemblyName); } // Element // // Retrieve information specific to the element // public ElementInformation ElementInformation { get { if (_evaluationElement == null) { _evaluationElement = new ElementInformation(this); } return _evaluationElement; } } // EvaluationContext // // Retrieve information specific to the context of how we are // being evaluated // protected ContextInformation EvaluationContext { get { if (_evalContext == null) { if (_configRecord == null) { // This is not associated with a context, so throw // failure throw new ConfigurationErrorsException( SR.GetString( SR.Config_element_no_context)); } _evalContext = new ContextInformation(_configRecord); } return _evalContext; } } internal protected virtual ConfigurationElementProperty ElementProperty { get { return _elementProperty; } } internal ConfigurationLockCollection UnMergeLockList( ConfigurationLockCollection sourceLockList, ConfigurationLockCollection parentLockList, ConfigurationSaveMode saveMode) { if (sourceLockList.ExceptionList == false) { switch (saveMode) { case ConfigurationSaveMode.Modified: { ConfigurationLockCollection tempLockList = new ConfigurationLockCollection(this, sourceLockList.LockType); foreach (string lockedAttributeName in sourceLockList) if (!parentLockList.Contains(lockedAttributeName) || sourceLockList.IsValueModified(lockedAttributeName)) { tempLockList.Add(lockedAttributeName, ConfigurationValueFlags.Default); } return tempLockList; } case ConfigurationSaveMode.Minimal: { ConfigurationLockCollection tempLockList = new ConfigurationLockCollection(this, sourceLockList.LockType); foreach (string lockedAttributeName in sourceLockList) if (!parentLockList.Contains(lockedAttributeName)) { tempLockList.Add(lockedAttributeName, ConfigurationValueFlags.Default); } return tempLockList; } } } else { // exception list write out the entire collection unless the entire collection // came from the parent. if (saveMode == ConfigurationSaveMode.Modified || saveMode == ConfigurationSaveMode.Minimal) { bool sameAsParent = false; if (sourceLockList.Count == parentLockList.Count) { sameAsParent = true; foreach (string lockedAttributeName in sourceLockList) { if (!parentLockList.Contains(lockedAttributeName) || (sourceLockList.IsValueModified(lockedAttributeName) && saveMode == ConfigurationSaveMode.Modified)) { sameAsParent = false; } } } if (sameAsParent == true) { return null; } } } return sourceLockList; } // // Return true if an attribute is one of our reserved locking attributes, // false otherwise. // internal static bool IsLockAttributeName(string name) { // optimize for common case that attribute name does not start with "lock" if (!StringUtil.StartsWith(name, "lock")) { return false; } foreach (string lockAttributeName in s_lockAttributeNames) { if (name == lockAttributeName) { return true; } } return false; } protected bool HasContext { get { return _configRecord != null; } } public Configuration CurrentConfiguration { get { return (_configRecord==null) ? null : _configRecord.CurrentConfiguration; } } } }