File: Settings\SettingsGroup.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Configuration\NuGet.Configuration.csproj (NuGet.Configuration)
// Copyright(c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Xml.Linq;
using NuGet.Shared;

namespace NuGet.Configuration
{
    public abstract class SettingsGroup<T> : SettingElement, ISettingsGroup where T : SettingElement
    {
        protected IList<T> Children { get; private set; }

        protected virtual bool CanBeCleared => true;

        public override string ElementName { get; }

        protected SettingsGroup(string name)
            : this(name, attributes: null, children: null)
        {
        }

        protected SettingsGroup(string name, IReadOnlyDictionary<string, string>? attributes, IEnumerable<T>? children)
            : base(attributes)
        {
            ElementName = name ?? throw new ArgumentNullException(message: Resources.Argument_Cannot_Be_Null_Or_Empty, paramName: nameof(name));
            if (children == null)
            {
                Children = new List<T>();
            }
            else
            {
                Children = new List<T>(children);
            }
        }

        public override bool IsEmpty() => !Children.Any() || Children.All(c => c.IsEmpty());

        internal SettingsGroup(string name, XElement element, SettingsFile origin)
            : base(element, origin)
        {
            ElementName = name;

            Children = SettingFactory.ParseChildren<T>(element, origin, CanBeCleared).ToList();

            foreach (var child in Children)
            {
                child.Parent = this;
            }
        }

        internal override XNode AsXNode()
        {
            if (Node is XElement)
            {
                return Node;
            }

            var element = new XElement(XmlUtility.GetEncodedXMLName(ElementName), Children.Select(c => c.AsXNode()));

            foreach (var attr in Attributes)
            {
                element.SetAttributeValue(attr.Key, attr.Value);
            }

            return element;
        }

        internal override void SetOrigin(SettingsFile origin)
        {
            base.SetOrigin(origin);

            foreach (var child in Children)
            {
                child.SetOrigin(origin);
            }
        }

        internal override void RemoveFromSettings()
        {
            base.RemoveFromSettings();

            foreach (var child in Children)
            {
                child.RemoveFromSettings();
            }
        }

        internal virtual bool Add(T setting)
        {
            if (setting == null)
            {
                throw new ArgumentNullException(nameof(setting));
            }

            if (Origin is null)
            {
                throw new InvalidOperationException("Cannot call this method on a setting where Origin is null.");
            }

            if (Origin.IsMachineWide)
            {
                throw new InvalidOperationException(Resources.CannotUpdateMachineWide);
            }

            if (Origin.IsReadOnly)
            {
                throw new InvalidOperationException(Resources.CannotUpdateReadOnlyConfig);
            }

            if (!Children.Contains(setting) && !setting.IsEmpty())
            {
                Children.Add(setting);

                setting.SetOrigin(Origin);
                setting.SetNode(setting.AsXNode());

                XElementUtility.AddIndented(Node as XElement, setting.Node);
                Origin.IsDirty = true;

                setting.Parent = this;

                return true;
            }

            return false;
        }

        internal virtual void Remove(T setting)
        {
            if (setting == null)
            {
                throw new ArgumentNullException(nameof(setting));
            }

            if (Origin != null && Origin.IsMachineWide)
            {
                throw new InvalidOperationException(Resources.CannotUpdateMachineWide);
            }

            if (Origin != null && Origin.IsReadOnly)
            {
                throw new InvalidOperationException(Resources.CannotUpdateReadOnlyConfig);
            }

            if (TryGetChild(setting, out var currentSetting) && Children.Remove(currentSetting))
            {
                currentSetting.RemoveFromSettings();

                if (Parent != null && IsEmpty())
                {
                    Parent.Remove(this);
                }
            }
        }

        protected bool TryGetChild(T expectedChild, [NotNullWhen(true)] out T? currentChild)
        {
            currentChild = null;

            foreach (var child in Children)
            {
                if (child.Equals(expectedChild))
                {
                    currentChild = child;

                    return true;
                }
            }

            return false;
        }

        void ISettingsGroup.Remove(SettingElement setting)
        {
            Remove((T)setting);
        }
    }
}