/////////////////////////////////////////////////////////////////////////// // 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.Xml.Linq; namespace wasStitchNET.Patchers { public static class Utilities { public static IEnumerable ParentsAndSelf(this XElement e) { if (e == null) yield break; yield return e; foreach (var p in ParentsAndSelf(e.Parent)) { yield return p; } } /// /// Get the absolute XPath to a given XElement /// (e.g. "/people/person[6]/name[1]/last[1]"). /// /// /// The element to get the index of. /// public static string GetAbsoluteXPath(this XElement element) { if (element == null) throw new ArgumentNullException("element"); Func 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); } /// /// Get the index of the given XElement relative to its /// siblings with identical names. If the given element is /// the root, -1 is returned. /// /// /// The element to get the index of. /// 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."); } } }