using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace ChadSoft.Utils.Configy
{
///
/// Merges configuration files
///
public static class ConfigurationMerger
{
#region Variables
private static string _XmlVersion = "1.0";
private static string _XmlEncoding = "UTF-8";
private static string _XmlStandalone = null;
#endregion
#region Methods
#endregion
#region Static Methods
///
/// Merges two documents of the same type
///
/// The document containing the "master" template with default values.
/// The second document to merge.
/// The merged result of the two source documents
public static XmlDocument Merge(XmlDocument masterDocument, XmlDocument overrideDocument)
{
XmlDocument merged = new XmlDocument();
// Create & populate the root node
XmlElement rootNode = merged.CreateElement(masterDocument.DocumentElement.Name);
switch (rootNode.Name)
{
case "appSettings":
MergeNodes(
masterDocument.DocumentElement,
overrideDocument.DocumentElement,
"//add", "key",
ref rootNode);
break;
case "connectionStrings":
MergeNodes(
masterDocument.DocumentElement,
overrideDocument.DocumentElement,
"//add", "name",
ref rootNode);
break;
default:
throw new NotSupportedException(String.Format(
"Element name {0} is not a supported merge element!",
masterDocument.DocumentElement.Name));
}
// Add 'em to the document
merged.AppendChild(merged.CreateXmlDeclaration(_XmlVersion, _XmlEncoding, _XmlStandalone));
merged.AppendChild(rootNode);
return merged;
}
///
/// Merges two nodes of the same type
///
/// The first node to merge.
/// The second node to merge.
/// The XPath query used to select the nodes being merged.
/// The id key.
/// The doc.
/// The value key.
///
/// The merged result of the two source nodes
///
internal static bool MergeNodes(XmlNode masterParent, XmlNode overrideParent, string xPath, string idKey, ref XmlElement destination)
{
XmlDocument doc = destination.OwnerDocument;
XmlNode source2Node;
XmlElement tmpNode;
foreach (XmlNode source1Node in masterParent.SelectNodes(xPath))
{
source2Node = overrideParent.SelectSingleNode(
String.Format("{0}[@{1}='{2}']", xPath, idKey, source1Node.Attributes[idKey].Value));
if (source2Node != null)
{
tmpNode = doc.CreateElement(source1Node.Name, source1Node.NamespaceURI);
if (MergeAttributes(source1Node as XmlElement, source2Node as XmlElement, ref tmpNode))
destination.AppendChild(tmpNode);
}
}
return true;
}
public static bool MergeAttributes(XmlElement masterElement, XmlElement overrideElement, ref XmlElement destination)
{
XmlElement overrideClone = overrideElement.Clone() as XmlElement;
// Overwrite any duplicate attributes
XmlAttribute tmpAttribute;
foreach (XmlAttribute attr in masterElement.Attributes)
{
// Create a new attribute
tmpAttribute = destination.OwnerDocument.CreateAttribute(attr.Name, attr.NamespaceURI);
// If the override clone has this attribute, overwrite the master attribute
if (overrideClone.HasAttribute(attr.Name, attr.NamespaceURI))
{
attr.Value = overrideClone.GetAttribute(attr.Name, attr.NamespaceURI);
overrideClone.RemoveAttribute(attr.Name, attr.NamespaceURI);
}
// Set the value on the new attribute
tmpAttribute.Value = attr.Value;
// Append the attribute to the destination node
destination.Attributes.Append(tmpAttribute);
}
// Add any additional attributes on the second element that weren't copied over
foreach (XmlAttribute attr in overrideClone.Attributes)
{
destination.Attributes.Append(
(XmlAttribute)destination.OwnerDocument.ImportNode(attr, false));
}
return true;
}
#endregion
}
}