File: Utility\XElementUtility.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.Linq;
using System.Xml.Linq;

namespace NuGet.Configuration
{
    internal static class XElementUtility
    {
        internal static string? GetOptionalAttributeValue(XElement element, string localName)
        {
            var attr = element.Attribute(localName);
            return attr?.Value;
        }

        internal static string? GetOptionalAttributeValue(XElement element, string localName, string namespaceName)
        {
            var attr = element.Attribute(XName.Get(localName, namespaceName));
            return attr?.Value;
        }

        internal static void AddIndented(XContainer? container, XNode? content)
        {
            if (container != null && content != null)
            {
                var oneIndentLevel = ComputeOneLevelOfIndentation(container);

                var leadingText = container.PreviousNode as XText;
                var parentIndent = leadingText != null ? leadingText.Value : Environment.NewLine;

                IndentChildrenElements(content as XContainer, parentIndent + oneIndentLevel, oneIndentLevel);

                AddLeadingIndentation(container, parentIndent, oneIndentLevel);
                container.Add(content);
                AddTrailingIndentation(container, parentIndent);
            }
        }

        internal static void RemoveIndented(XNode? element)
        {
            if (element != null)
            {
                // NOTE: this method is tested by BindinRedirectManagerTest and SettingsTest
                var textBeforeOrNull = element.PreviousNode as XText;
                var textAfterOrNull = element.NextNode as XText;
                var oneIndentLevel = ComputeOneLevelOfIndentation(element);
                var isLastChild = !element.ElementsAfterSelf().Any();

                element.Remove();

                if (textAfterOrNull != null
                    && IsWhiteSpace(textAfterOrNull))
                {
                    textAfterOrNull.Remove();
                }

                if (isLastChild
                    && textBeforeOrNull != null
                    && (textAfterOrNull is null || IsWhiteSpace(textAfterOrNull)))
                {
                    textBeforeOrNull.Value = textBeforeOrNull.Value.Substring(0, textBeforeOrNull.Value.Length - oneIndentLevel.Length);
                }
            }
        }

        private static string ComputeOneLevelOfIndentation(XNode node)
        {
            var depth = node.Ancestors().Count();
            var textBeforeOrNull = node.PreviousNode as XText;
            if (depth == 0
                || textBeforeOrNull == null
                || !IsWhiteSpace(textBeforeOrNull))
            {
                return "  ";
            }

            var indentString = textBeforeOrNull.Value.Trim(Environment.NewLine.ToCharArray());
            var lastChar = indentString.LastOrDefault();
            var indentChar = (lastChar == '\t' ? '\t' : ' ');
            var indentLevel = Math.Max(1, indentString.Length / depth);
            return new string(indentChar, indentLevel);
        }

        private static bool IsWhiteSpace(XText textNode)
        {
            return string.IsNullOrWhiteSpace(textNode.Value);
        }

        private static void IndentChildrenElements(XContainer? container, string containerIndent, string oneIndentLevel)
        {
            if (container != null)
            {
                var childIndent = containerIndent + oneIndentLevel;
                foreach (var element in container.Elements())
                {
                    element.AddBeforeSelf(new XText(childIndent));
                    IndentChildrenElements(element, childIndent + oneIndentLevel, oneIndentLevel);
                }

                if (container.Elements().Any())
                {
                    container.Add(new XText(containerIndent));
                }
            }
        }

        private static void AddLeadingIndentation(XContainer container, string containerIndent, string oneIndentLevel)
        {
            var containerIsSelfClosed = !container.Nodes().Any();
            var lastChildText = container.LastNode as XText;
            if (containerIsSelfClosed || lastChildText == null)
            {
                container.Add(new XText(containerIndent + oneIndentLevel));
            }
            else
            {
                lastChildText.Value += oneIndentLevel;
            }
        }

        private static void AddTrailingIndentation(XContainer container, string containerIndent)
        {
            container.Add(new XText(containerIndent));
        }
    }
}