wasStitchNET – Rev 7

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
// Based on: https://seattlesoftware.wordpress.com/2009/03/13/get-the-xpath-to-an-xml-element-xelement/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;

namespace wasStitchNET.Patchers
{
    public static class Utilities
    {

        /// <summary> 
        /// Get the absolute XPath to a given XElement
        /// (e.g. "/people/person[6]/name[1]/last[1]").
        /// </summary> 
        /// <param name="element"> 
        /// The element to get the index of.
        /// </param> 
        public static string GetAbsoluteXPath(this XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            Func<XElement, string> relativeXPath = e =>
            {
                var index = e.IndexPosition();
                var name = e.Name.LocalName;

                // If the element is the root, no index is required

                return (index == -1) ? "/" + name : string.Format
                (
                    "/{0}[{1}]",
                    name,
                    index.ToString()
                );
            };

            var ancestors = from e in element.Ancestors()
                            select relativeXPath(e);

            return string.Concat(ancestors.Reverse().ToArray()) +
                   relativeXPath(element);
        }

        /// <summary> 
        /// Get the index of the given XElement relative to its
        /// siblings with identical names. If the given element is
        /// the root, -1 is returned.
        /// </summary> 
        /// <param name="element"> 
        /// The element to get the index of.
        /// </param> 
        public static int IndexPosition(this XElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            if (element.Parent == null)
            {
                return -1;
            }

            var i = 1; // Indexes for nodes start at 1, not 0

            foreach (var sibling in element.Parent.Elements(element.Name))
            {
                if (sibling == element)
                {
                    return i;
                }

                i++;
            }

            throw new InvalidOperationException
                ("element has been removed from its parent.");
        }

    }
}