File: System\Configuration\SectionRecord.cs
Web Access
Project: src\src\libraries\System.Configuration.ConfigurationManager\src\System.Configuration.ConfigurationManager.csproj (System.Configuration.ConfigurationManager)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
 
namespace System.Configuration
{
    [DebuggerDisplay("SectionRecord {ConfigKey}")]
    internal sealed class SectionRecord
    {
        //
        // 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 FlagLocked = 0x00000001;
 
        // lock children of this section
        private const int FlagLockChildren = 0x00000002;
 
        // propagation of FactoryRecord.RequirePermission
        private const int FlagRequirePermission = 0x00000008;
 
        // Look at AddLocationInput for explanation of this flag's purpose
        private const int FlagLocationInputLockApplied = 0x00000010;
 
        // Look at AddIndirectLocationInput for explanation of this flag's purpose
        private const int FlagIndirectLocationInputLockApplied = 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 FlagChildrenLockWithoutFileInput = 0x00000040;
 
        //
        // Designtime flags at or above 0x00010000
        //
 
        // the section has been added to the update list
        private const int FlagAddUpdate = 0x00010000;
 
        // result can be null, so we use this object to indicate whether it has been evaluated
        private static readonly object s_unevaluated = new object();
 
        // config key
 
        // The input from this file
 
        private SafeBitVector32 _flags;
 
        // 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.
 
        // The input from location sections
        // This list is ordered to keep oldest ancestors at the front
 
        // the cached result of evaluating this section
 
        // the cached result of evaluating this section after GetRuntimeObject is called
 
 
        internal SectionRecord(string configKey)
        {
            ConfigKey = configKey;
            Result = s_unevaluated;
            ResultRuntimeObject = s_unevaluated;
        }
 
        internal string ConfigKey { get; }
 
        internal bool Locked => _flags[FlagLocked];
 
        internal bool LockChildren => _flags[FlagLockChildren];
 
        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[FlagChildrenLockWithoutFileInput];
 
                return result;
            }
        }
 
        internal bool RequirePermission
        {
            get { return _flags[FlagRequirePermission]; }
            set { _flags[FlagRequirePermission] = value; }
        }
 
        internal bool AddUpdate
        {
            get { return _flags[FlagAddUpdate]; }
            set { _flags[FlagAddUpdate] = value; }
        }
 
        internal bool HasLocationInputs => (LocationInputs != null) && (LocationInputs.Count > 0);
 
        internal List<SectionInput> LocationInputs { get; private set; }
 
        internal SectionInput LastLocationInput => HasLocationInputs ? LocationInputs[LocationInputs.Count - 1] : null;
 
        internal bool HasFileInput => FileInput != null;
 
        internal SectionInput FileInput { get; private set; }
 
        internal bool HasIndirectLocationInputs
            => (IndirectLocationInputs != null) && (IndirectLocationInputs.Count > 0);
 
        internal List<SectionInput> IndirectLocationInputs { get; private set; }
 
        internal SectionInput LastIndirectLocationInput
            => HasIndirectLocationInputs ? IndirectLocationInputs[IndirectLocationInputs.Count - 1] : null;
 
        internal bool HasInput => HasLocationInputs || HasFileInput || HasIndirectLocationInputs;
 
        internal bool HasResult => Result != s_unevaluated;
 
        internal bool HasResultRuntimeObject => ResultRuntimeObject != s_unevaluated;
 
        internal object Result { get; set; }
 
        internal object ResultRuntimeObject { get; set; }
 
        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;
 
                return HasFileInput && FileInput.HasErrors;
            }
        }
 
        internal void
            AddLocationInput(SectionInput sectionInput)
        {
            AddLocationInputImpl(sectionInput, false);
        }
 
        internal void ChangeLockSettings(OverrideMode forSelf, OverrideMode forChildren)
        {
            if (forSelf != OverrideMode.Inherit)
            {
                _flags[FlagLocked] = forSelf == OverrideMode.Deny;
                _flags[FlagLockChildren] = forSelf == OverrideMode.Deny;
            }
 
            if (forChildren != OverrideMode.Inherit)
                _flags[FlagLockChildren] = (forSelf == OverrideMode.Deny) || (forChildren == OverrideMode.Deny);
        }
 
        // AddFileInput
        internal void AddFileInput(SectionInput sectionInput)
        {
            Debug.Assert(sectionInput != null);
 
            FileInput = sectionInput;
 
            // If the file input has an explicit value for its children locking - use it
            // Note we dont change the current lock setting
            if (!sectionInput.HasErrors &&
                (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[FlagChildrenLockWithoutFileInput] = 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[FlagLockChildren] = Locked;
            }
        }
 
        internal void
            AddIndirectLocationInput(SectionInput sectionInput)
        {
            AddLocationInputImpl(sectionInput, true);
        }
 
        private void
            AddLocationInputImpl(SectionInput sectionInput, bool isIndirectLocation)
        {
            List<SectionInput> inputs = isIndirectLocation
                ? IndirectLocationInputs
                : LocationInputs;
 
            int flag = isIndirectLocation
                ? FlagIndirectLocationInputLockApplied
                : FlagLocationInputLockApplied;
 
            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 coming 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 && !_flags[flag])
            {
                OverrideMode modeLocation = sectionInput.SectionXmlInfo.OverrideModeSetting.OverrideMode;
 
                if (modeLocation != OverrideMode.Inherit)
                {
                    ChangeLockSettings(modeLocation, modeLocation);
                    _flags[flag] = true;
                }
            }
        }
 
        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 void ClearResult()
        {
            FileInput?.ClearResult();
 
            if (LocationInputs != null) foreach (SectionInput input in LocationInputs) input.ClearResult();
 
            Result = s_unevaluated;
            ResultRuntimeObject = s_unevaluated;
        }
 
        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 void ThrowOnErrors()
        {
            if (HasErrors) throw new ConfigurationErrorsException(GetAllErrors());
        }
    }
}