/////////////////////////////////////////////////////////////////////////// // 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. // /////////////////////////////////////////////////////////////////////////// using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using System.Xml.XPath; namespace wasStitchNET.Patchers { public static class XML { public static XDocument PatchXDocument(XDocument cfg, XDocument nfg, HashSet forceXPaths, HashSet excludeXPaths) { // Select all distinct paths in the new configuration without paths to exclude and with paths to force. foreach (var e in nfg .Descendants() .Where(e => !excludeXPaths.Any(o => e.GetAbsoluteXPath().Contains(o))) .Concat( nfg .Descendants() .Where(e => forceXPaths.Any(o => e.GetAbsoluteXPath().Contains(o)))) .Distinct()) { // Get the XPath to this element. var xpath = e.GetAbsoluteXPath(); // Select the element in the current configuration that is found in the default configuration. var cfgElement = cfg.XPathSelectElement(xpath); switch (cfgElement != null) { // Element found in current configuration. case true: // Only set the element value if it has no descendants and it contained in the force paths. if (!cfgElement.Descendants().Any() && forceXPaths.Any(o => xpath.Contains(o))) cfgElement.Value = e.Value; break; // Element not found in the current configuration. default: // Find the first existing parent of the default configuration in the current configuration. var parent = e; do { var cfgParentElement = cfg.XPathSelectElement(parent.GetAbsoluteXPath()); if (cfgParentElement != null) { // Add the default configuration parent to the current configuration. cfgParentElement.Add( nfg.XPathSelectElement(cfgParentElement.GetAbsoluteXPath())); break; } parent = parent.Parent; } while (parent != null); break; } } return cfg; } } }