File: System\Configuration\SectionUpdates.cs
Project: ndp\fx\src\Configuration\System.Configuration.csproj (System.Configuration)
//------------------------------------------------------------------------------
// <copyright file="SectionUpdates.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Configuration {
    using System.Collections;
 
    //
    // A collection of updates to the sections that have the same location attributes.
    //
    internal class SectionUpdates {
        private string      _name;          // name of this section, for debugging
        private Hashtable   _groups;        // group name -> SectionUpdates
        private Hashtable   _sections;      // section name -> Update
        private int         _cUnretrieved;  // number of items not retrieved in update algorithm.
        private int         _cMoved;        // number of items moved (new or location attributes changed).
        private Update      _sectionGroupUpdate;   // update for this group
        private bool        _isNew;         // is the entire section new (all sections and subgroups)?
 
        internal SectionUpdates(string name) {
            _name = name;
            _groups = new Hashtable();
            _sections = new Hashtable();
        }
 
        internal bool IsNew {
            get {return _isNew;}
            set {_isNew = value;}
        }
 
        internal bool IsEmpty {
            get {
                return _groups.Count == 0 && _sections.Count == 0;
            }
        }
 
        //
        // Find the SectionUpdates for a configKey, and create it if it does not exist.
        //
        private SectionUpdates FindSectionUpdates(string configKey, bool isGroup) {
            string group, dummy;
 
            if (isGroup) {
                group = configKey;
            }
            else {
                BaseConfigurationRecord.SplitConfigKey(configKey, out group, out dummy);
            }
 
            Debug.Assert(String.IsNullOrEmpty(_name), "FindSectionUpdates assumes search is from root record");
            SectionUpdates sectionUpdates = this;
            if (group.Length != 0) {
                // find the SectionUpdates for the group
                string [] groups = group.Split(BaseConfigurationRecord.ConfigPathSeparatorParams);
                foreach (string groupPart in groups) {
                    SectionUpdates sectionUpdatesChild = (SectionUpdates) sectionUpdates._groups[groupPart];
                    if (sectionUpdatesChild == null) {
                        sectionUpdatesChild = new SectionUpdates(groupPart);
                        sectionUpdates._groups[groupPart] = sectionUpdatesChild;
                    }
 
                    sectionUpdates = sectionUpdatesChild;
                }
            }
 
            return sectionUpdates;
        }
 
        //
        // Recursively check whether this group has all new updates.
        // An update is new if all sections are new and all subgroups are new.
        //
        internal void CompleteUpdates() {
            bool allSubgroupsAreNew = true;
 
            // call CompleteUpdates() for all children
            foreach (SectionUpdates sectionUpdates in _groups.Values) {
                sectionUpdates.CompleteUpdates();
                if (!sectionUpdates.IsNew) {
                    allSubgroupsAreNew = false;
                }
            }
            
            _isNew = allSubgroupsAreNew && _cMoved == _sections.Count;
        }
 
        //
        // Add one update.
        //
        internal void AddSection(Update update) {
            SectionUpdates sectionUpdates = FindSectionUpdates(update.ConfigKey, false);
            sectionUpdates._sections.Add(update.ConfigKey, update);
 
            // Maintain counts.
            sectionUpdates._cUnretrieved++;
            if (update.Moved) {
                sectionUpdates._cMoved++;
            }
        }
 
        //
        // Add a section group update.
        //
        internal void AddSectionGroup(Update update) {
            SectionUpdates sectionUpdates = FindSectionUpdates(update.ConfigKey, true);
            sectionUpdates._sectionGroupUpdate = update;
        }
 
        //
        // Retrieve an update if it has not yet been retrieved.
        //
        private Update GetUpdate(string configKey) {
            Update update = (Update) _sections[configKey];
            if (update != null) {
                if (update.Retrieved) {
                    update = null;
                }
                else {
                    update.Retrieved = true;
                    _cUnretrieved--;
                    if (update.Moved) {
                        _cMoved--;
                    }
                }
            }
 
            return update;
        }
 
        //
        // Get the update for a section group.
        //
        internal DeclarationUpdate GetSectionGroupUpdate() {
            if (_sectionGroupUpdate != null && !_sectionGroupUpdate.Retrieved) {
                _sectionGroupUpdate.Retrieved = true;
                return (DeclarationUpdate) _sectionGroupUpdate;
            }
 
            return null;
        }
 
        internal DefinitionUpdate GetDefinitionUpdate(string configKey) {
            return (DefinitionUpdate) GetUpdate(configKey);
        }
 
        internal DeclarationUpdate GetDeclarationUpdate(string configKey) {
            return (DeclarationUpdate) GetUpdate(configKey);
        }
 
        internal SectionUpdates GetSectionUpdatesForGroup(string group) {
            return (SectionUpdates) _groups[group];
        }
 
        //
        // Return true if this section group or any of its children have unretrieved sections.
        //
        internal bool HasUnretrievedSections() {
            if (_cUnretrieved > 0 || (_sectionGroupUpdate != null && !_sectionGroupUpdate.Retrieved)) {
                return true;
            }
 
            foreach (SectionUpdates sectionUpdates in _groups.Values) {
                if (sectionUpdates.HasUnretrievedSections()) {
                    return true;
                }
            }
 
            return false;
        }
        internal void MarkAsRetrieved()
        {
            _cUnretrieved = 0;
            foreach (SectionUpdates sectionUpdates in _groups.Values) {
                sectionUpdates.MarkAsRetrieved();
            }
            if (_sectionGroupUpdate != null){
                _sectionGroupUpdate.Retrieved = true;
            }
        }
 
        internal void MarkGroupAsRetrieved(string groupName)
        {
            SectionUpdates sectionUpdates = _groups[groupName] as SectionUpdates;
            if (sectionUpdates != null) {
                sectionUpdates.MarkAsRetrieved();
            }
        }
        //
        // Return true if this section group contains any new section groups, false otherwise.
        //
        internal bool HasNewSectionGroups() 
        {
            foreach (SectionUpdates sectionUpdates in _groups.Values) {
                if (sectionUpdates.IsNew)
                    return true;
            }
 
            return false;
        }
 
        //
        // Return a sorted list of the names of unretrieved sections in this group.
        //
        internal string[] GetUnretrievedSectionNames() {
            if (_cUnretrieved == 0)
                return null;
 
            string[] sectionNames = new string[_cUnretrieved];
            int i = 0;
            foreach (Update update in _sections.Values) {
                if (!update.Retrieved) {
                    sectionNames[i] = update.ConfigKey;
                    i++;
                }
            }
 
            Array.Sort(sectionNames);
            return sectionNames;
        }
 
        //
        // Return a sorted list of the names of moved and unretrieved sections in this group.
        //
        internal string[] GetMovedSectionNames() {
            if (_cMoved == 0)
                return null;
 
            string[] sectionNames = new string[_cMoved];
            int i = 0;
            foreach (Update update in _sections.Values) {
                if (update.Moved && !update.Retrieved) {
                    sectionNames[i] = update.ConfigKey;
                    i++;
                }
            }
 
            Array.Sort(sectionNames);
            return sectionNames;
        }
 
        //
        // Return a sorted list of the names of groups with unretrieved sections.
        //
        internal string[] GetUnretrievedGroupNames() {
            ArrayList unretrievedGroups = new ArrayList();
 
            foreach (DictionaryEntry de in _groups) {
                string group = (string) de.Key;
                SectionUpdates sectionUpdates = (SectionUpdates) de.Value;
                if (sectionUpdates.HasUnretrievedSections()) {
                    unretrievedGroups.Add(group);
                }
            }
 
            if (unretrievedGroups.Count == 0)
                return null;
 
            string[] groupNames = new string[unretrievedGroups.Count];
            unretrievedGroups.CopyTo(groupNames);
            Array.Sort(groupNames);
            return groupNames;
        }
 
        //
        // Return a sorted list of the names of new section groups with unretrieved sections.
        //
        internal string[] GetNewGroupNames() {
            ArrayList newsGroups = new ArrayList();
 
            foreach (DictionaryEntry de in _groups) {
                string group = (string) de.Key;
                SectionUpdates sectionUpdates = (SectionUpdates) de.Value;
                if (sectionUpdates.IsNew && sectionUpdates.HasUnretrievedSections()) {
                    newsGroups.Add(group);
                }
            }
 
            if (newsGroups.Count == 0)
                return null;
 
            string[] groupNames = new string[newsGroups.Count];
            newsGroups.CopyTo(groupNames);
            Array.Sort(groupNames);
            return groupNames;
        }
    }
}