File: src\Compilers\Core\Portable\InternalUtilities\XmlUtilities.cs
Web Access
Project: src\src\Workspaces\Core\MSBuild.BuildHost\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj (Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
 
namespace Roslyn.Utilities
{
    internal static class XmlUtilities
    {
        internal static TNode Copy<TNode>(this TNode node, bool copyAttributeAnnotations)
            where TNode : XNode
        {
            XNode copy;
 
            // Documents can't be added to containers, so our usual copy trick won't work.
            if (node.NodeType == XmlNodeType.Document)
            {
                copy = new XDocument(((XDocument)(object)node));
            }
            else
            {
                XContainer temp = new XElement("temp");
                temp.Add(node);
                copy = temp.LastNode!;
                temp.RemoveNodes();
            }
 
            Debug.Assert(copy != node);
            Debug.Assert(copy.Parent == null); // Otherwise, when we give it one, it will be copied.
 
            // Copy annotations, the above doesn't preserve them.
            // We need to preserve Location annotations as well as line position annotations.
            CopyAnnotations(node, copy);
 
            // We also need to preserve line position annotations for all attributes
            // since we report errors with attribute locations.
            if (copyAttributeAnnotations && node.NodeType == XmlNodeType.Element)
            {
                var sourceElement = ((XElement)(object)node);
                var targetElement = ((XElement)copy);
 
                IEnumerator<XAttribute> sourceAttributes = sourceElement.Attributes().GetEnumerator();
                IEnumerator<XAttribute> targetAttributes = targetElement.Attributes().GetEnumerator();
                while (sourceAttributes.MoveNext() && targetAttributes.MoveNext())
                {
                    Debug.Assert(sourceAttributes.Current.Name == targetAttributes.Current.Name);
                    CopyAnnotations(sourceAttributes.Current, targetAttributes.Current);
                }
            }
 
            return (TNode)copy;
        }
 
        private static void CopyAnnotations(XObject source, XObject target)
        {
            foreach (var annotation in source.Annotations<object>())
            {
                target.AddAnnotation(annotation);
            }
        }
 
        internal static XElement[]? TrySelectElements(XNode node, string xpath, out string? errorMessage, out bool invalidXPath)
        {
            errorMessage = null;
            invalidXPath = false;
 
            try
            {
                var xpathResult = System.Xml.XPath.Extensions.XPathSelectElements(node, xpath);
 
                // Throws InvalidOperationException if the result of the XPath is an XDocument:
                return xpathResult?.ToArray();
            }
            catch (InvalidOperationException e)
            {
                errorMessage = e.Message;
                return null;
            }
            catch (Exception e) when (e.GetType().FullName == "System.Xml.XPath.XPathException")
            {
                errorMessage = e.Message;
                invalidXPath = true;
                return null;
            }
        }
    }
}