File: System\Configuration\SectionRecord.cs
Project: ndp\fx\src\Configuration\System.Configuration.csproj (System.Configuration)
//------------------------------------------------------------------------------
// <copyright file="SectionRecord.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Configuration {
    using System.Configuration.Internal;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Text;
    using System.Threading;
    using System.Reflection;
    using System.Xml;
 
    [System.Diagnostics.DebuggerDisplay("SectionRecord {ConfigKey}")]
    internal class SectionRecord {
        //
        // Flags constants
        //
 
        //
        // Runtime flags below 0x10000
        //
 
        // locked by parent input, either because a parent section is locked,
        // a parent section locks all children, or a location input for this 
        // configPath has allowOverride=false.
        private const int Flag_Locked                               = 0x00000001;
 
        // lock children of this section
        private const int Flag_LockChildren                         = 0x00000002;
 
        // propagation of FactoryRecord.IsFactoryTrustedWithoutAptca
        private const int Flag_IsResultTrustedWithoutAptca          = 0x00000004;
 
        // propagation of FactoryRecord.RequirePermission
        private const int Flag_RequirePermission                    = 0x00000008;
 
        // Look at AddLocationInput for explanation of this flag's purpose
        private const int Flag_LocationInputLockApplied             = 0x00000010;
 
        // Look at AddIndirectLocationInput for explanation of this flag's purpose
        private const int Flag_IndirectLocationInputLockApplied     = 0x00000020;
 
        // The flag gives us the inherited lock mode for this section record without the file input
        // We need this to support SectionInformation.OverrideModeEffective.
        private const int Flag_ChildrenLockWithoutFileInput         = 0x00000040;
 
        //
        // Designtime flags at or above 0x00010000
        //
 
        // the section has been added to the update list
        private const int Flag_AddUpdate                            = 0x00010000;
 
        // result can be null, so we use this object to indicate whether it has been evaluated
        static object                           s_unevaluated = new object();
 
        private SafeBitVector32                _flags;
 
        // config key
        private string                          _configKey;
 
        // The input from location sections
        // This list is ordered to keep oldest ancestors at the front
        private List<SectionInput>              _locationInputs;    
 
        // The input from this file
        private SectionInput                    _fileInput;
 
        // This special input is used only when creating a location config record.
        // The inputs are from location tags which are found in the same config file as the
        // location config configPath, but point to the parent paths of the location config
        // configPath.  See the comment for VSWhidbey 540184 in Init() in
        // BaseConfigurationRecord.cs for more details.
        private List<SectionInput>              _indirectLocationInputs;
 
        // the cached result of evaluating this section
        private object                          _result;
        
        // the cached result of evaluating this section after GetRuntimeObject is called
        private object                          _resultRuntimeObject;
 
 
        internal SectionRecord(string configKey) {
            _configKey = configKey;
            _result = s_unevaluated;
            _resultRuntimeObject = s_unevaluated;
        }
 
        internal string ConfigKey {
            get {return _configKey;}
        }
 
        internal bool Locked {
            get {return _flags[Flag_Locked];}
        }
 
        internal bool LockChildren {
            get {return _flags[Flag_LockChildren];}
        }
 
        internal bool LockChildrenWithoutFileInput {
            get {
 
                // Start assuming we dont have a file input
                // When we don't have file input the lock mode for children is the same for LockChildren and LockChildrenWithoutFileInput
                bool result = LockChildren;
 
                if (HasFileInput) {
                    result =  _flags[Flag_ChildrenLockWithoutFileInput];
                }
 
                return result;
            }
        }
 
        internal bool IsResultTrustedWithoutAptca {
            get {return _flags[Flag_IsResultTrustedWithoutAptca];}
            set {_flags[Flag_IsResultTrustedWithoutAptca] = value;}
        }
 
        internal bool RequirePermission {
            get {return _flags[Flag_RequirePermission];}
            set {_flags[Flag_RequirePermission] = value;}
        }
 
        internal bool AddUpdate {
            get {return _flags[Flag_AddUpdate];}
            set {_flags[Flag_AddUpdate] = value;}
        }
 
        internal bool HasLocationInputs {
            get {
                return _locationInputs != null && _locationInputs.Count > 0;
            }
        }
 
        internal List<SectionInput> LocationInputs {
            get {return _locationInputs;}
        }
 
        internal SectionInput LastLocationInput {
            get {
                if (HasLocationInputs) {
                    return _locationInputs[_locationInputs.Count - 1];
                }
                else {
                    return null;
                }
            }
        }
 
        internal void 
        AddLocationInput(SectionInput sectionInput) {
            AddLocationInputImpl(sectionInput, false);
        }
 
        internal bool HasFileInput {
            get {
                return _fileInput != null;
            }
        }
 
        internal SectionInput FileInput {
            get { return _fileInput; }
        }
 
        internal void ChangeLockSettings(OverrideMode forSelf, OverrideMode forChildren) {
 
            if (forSelf != OverrideMode.Inherit) {
                _flags[Flag_Locked]         = (forSelf == OverrideMode.Deny);
                _flags[Flag_LockChildren]   = (forSelf == OverrideMode.Deny);
            }
 
            if (forChildren != OverrideMode.Inherit) {
                _flags[Flag_LockChildren] = ((forSelf == OverrideMode.Deny) || (forChildren == OverrideMode.Deny));
            }
        }
 
        // AddFileInput
        internal void AddFileInput(SectionInput sectionInput) {
            Debug.Assert(sectionInput != null);
        
            _fileInput = sectionInput;
 
            if (!sectionInput.HasErrors) {
                
                // If the file input has an explciti value for its children locking - use it
                // Note we dont change the current lock setting
                if (sectionInput.SectionXmlInfo.OverrideModeSetting.OverrideMode != OverrideMode.Inherit) {
 
                    // Store the current setting before applying the lock from the file input
                    // So that if the user changes the current OverrideMode on this configKey to "Inherit"
                    // we will know what we are going to inherit ( used in SectionInformation.OverrideModeEffective )
                    // Note that we cannot use BaseConfigurationRecord.ResolveOverrideModeFromParent as it gives us only the lock
                    // resolved up to our immediate parent which does not inlcude normal and indirect location imputs
                    _flags[Flag_ChildrenLockWithoutFileInput] = LockChildren;
 
                    ChangeLockSettings(OverrideMode.Inherit, sectionInput.SectionXmlInfo.OverrideModeSetting.OverrideMode);
                }
            }
        }
 
        internal void RemoveFileInput() {
            if (_fileInput != null) {
                _fileInput = null;
 
                // Reset LockChildren flag to the value provided by 
                // location input or inherited sections.
                _flags[Flag_LockChildren] = Locked;
            }
        }
 
        internal bool HasIndirectLocationInputs {
            get {
                return _indirectLocationInputs != null && _indirectLocationInputs.Count > 0;
            }
        }
 
        internal List<SectionInput> IndirectLocationInputs {
            get {return _indirectLocationInputs;}
        }
 
        internal SectionInput LastIndirectLocationInput {
            get {
                if (HasIndirectLocationInputs) {
                    return _indirectLocationInputs[_indirectLocationInputs.Count - 1];
                }
                else {
                    return null;
                }
            }
        }
 
        internal void 
        AddIndirectLocationInput(SectionInput sectionInput) {
            AddLocationInputImpl(sectionInput, true);
        }
 
        private void
        AddLocationInputImpl(SectionInput sectionInput, bool isIndirectLocation) {
            List<SectionInput>  inputs = isIndirectLocation ? 
                                        _indirectLocationInputs : 
                                        _locationInputs;
                                        
            int                 flag = isIndirectLocation ?
                                        Flag_IndirectLocationInputLockApplied :
                                        Flag_LocationInputLockApplied;
            
            if (inputs == null) {
                inputs = new List<SectionInput>(1);
                
                if (isIndirectLocation) {
                    _indirectLocationInputs = inputs;
                }
                else {
                    _locationInputs = inputs;
                }
            }
 
            // The list of locationSections is traversed from child to parent,
            // so insert at the beginning of the list.
            inputs.Insert(0, sectionInput);
 
            // Only the overrideMode from the parent thats closest to the SectionRecord has effect
            //
            // For location input:
            // Remember that this method will be called for location inputs comming from the immediate parent first
            // and then walking the hierarchy up to the root level
            //
            // For indirect location input:
            // This method will be first called for indirect input closest to the location config
            if (!sectionInput.HasErrors) {
                
                if (!_flags[flag]) {
                    OverrideMode modeLocation = sectionInput.SectionXmlInfo.OverrideModeSetting.OverrideMode;
 
                    if (modeLocation != OverrideMode.Inherit) { 
                        ChangeLockSettings(modeLocation, modeLocation);
                        _flags[flag] = true;   
                    }
                }
            }
        }
 
        internal bool HasInput {
            get {
                return HasLocationInputs || HasFileInput || HasIndirectLocationInputs;
            }
        }
 
        internal void ClearRawXml() {
            if (HasLocationInputs) {
                foreach (SectionInput locationInput in LocationInputs) {
                    locationInput.SectionXmlInfo.RawXml = null;
                }
            }
 
            if (HasIndirectLocationInputs) {
                foreach (SectionInput indirectLocationInput in IndirectLocationInputs) {
                    indirectLocationInput.SectionXmlInfo.RawXml = null;
                }
            }
 
            if (HasFileInput) {
                FileInput.SectionXmlInfo.RawXml = null;
            }
        }
 
        internal bool HasResult {
            get {return _result != s_unevaluated;}
        }
 
        internal bool HasResultRuntimeObject {
            get {return _resultRuntimeObject != s_unevaluated;}
        }
 
        internal object Result {
            get {
                // Useful assert, but it fires in the debugger when using automatic property evaluation
                // Debug.Assert(_result != s_unevaluated, "_result != s_unevaluated");
 
                return _result;
            }
 
            set {_result = value;}
        }
 
        internal object ResultRuntimeObject {
            get {
                // Useful assert, but it fires in the debugger when using automatic property evaluation
                // Debug.Assert(_resultRuntimeObject != s_unevaluated, "_resultRuntimeObject != s_unevaluated");
 
                return _resultRuntimeObject;
            }
 
            set {
                _resultRuntimeObject = value;
            }
        }
 
        internal void ClearResult() {
            if (_fileInput != null) {
                _fileInput.ClearResult();
            }
 
            if (_locationInputs != null) {
                foreach (SectionInput input in _locationInputs) {
                    input.ClearResult();
                }
            }
 
            _result = s_unevaluated;
            _resultRuntimeObject = s_unevaluated;
        }
 
        //
        // Error handling.
        //
 
        private List<ConfigurationException> GetAllErrors() {
            List<ConfigurationException> allErrors = null;
 
            if (HasLocationInputs) {
                foreach (SectionInput input in LocationInputs) {
                    ErrorsHelper.AddErrors(ref allErrors, input.Errors);
                }
            }
 
            if (HasIndirectLocationInputs) {
                foreach (SectionInput input in IndirectLocationInputs) {
                    ErrorsHelper.AddErrors(ref allErrors, input.Errors);
                }
            }
 
            if (HasFileInput) {
                ErrorsHelper.AddErrors(ref allErrors, FileInput.Errors);
            }
 
            return allErrors;
        }
 
        internal bool HasErrors {
            get {
                if (HasLocationInputs) {
                    foreach (SectionInput input in LocationInputs) {
                        if (input.HasErrors) {
                            return true;
                        }
                    }
                }
 
                if (HasIndirectLocationInputs) {
                    foreach (SectionInput input in IndirectLocationInputs) {
                        if (input.HasErrors) {
                            return true;
                        }
                    }
                }
 
                if (HasFileInput) {
                    if (FileInput.HasErrors) {
                        return true;
                    }
                }
 
                return false;
            }
        }
 
        internal void ThrowOnErrors() {
            if (HasErrors) {
                throw new ConfigurationErrorsException(GetAllErrors());
            }
        }
    }
}