using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Windows.Forms; namespace VEPROMS.CSLA.Library { public class Comparator { private XmlDocument _ResultsDoc; public XmlDocument ResultsDoc { get { return _ResultsDoc; } set { _ResultsDoc = value; } } private XmlDocument _XDoc1; public XmlDocument XDoc1 { get { return _XDoc1; } set { _XDoc1 = value; } } private XmlDocument _XDoc2; public XmlDocument XDoc2 { get { return _XDoc2; } set { _XDoc2 = value; } } public Comparator(XmlDocument xdoc1, XmlDocument xdoc2) { XDoc1 = xdoc1; XDoc2 = xdoc2; ResultsDoc = new XmlDocument(); ResultsDoc.LoadXml(@""); } public Comparator(string existingFC, string importedFC) //string fName1, string fName2) { XDoc1 = new XmlDocument(); XDoc1.LoadXml(existingFC); XDoc2 = new XmlDocument(); XDoc2.LoadXml(importedFC); ResultsDoc = new XmlDocument(); ResultsDoc.LoadXml(@""); } public XmlDocument Compare() { AllKeys = null; Compare(XDoc1.DocumentElement, XDoc2.DocumentElement, ""); Console.WriteLine("results xml = \r\n{0}", ResultsDoc.InnerXml); return ResultsDoc; } public void Compare(XmlNode xn1, XmlNode xn2, string path) { if (xn1.OuterXml == xn2.OuterXml) return; Compare(xn1.Attributes, xn2.Attributes , path, xn1, xn2); xn1.Attributes.RemoveAll(); xn2.Attributes.RemoveAll(); Dictionary xns1 = new Dictionary(); // child nodes, key = 'OuterXml', value = node itself Dictionary xns2 = new Dictionary(); // xns1 starts out with all child nodes of xn1 foreach (XmlNode xc1 in xn1.ChildNodes) xns1.Add(xc1.OuterXml + GetKey(xc1), xc1); // xns1 - remove any matching child nodes from xns2 // xns2 - has nodes that are not in xns1 foreach (XmlNode xc2 in xn2.ChildNodes) if (xns1.ContainsKey(xc2.OuterXml + GetKey(xc2))) xns1.Remove(xc2.OuterXml + GetKey(xc2)); else xns2.Add(xc2.OuterXml+GetKey(xc2), xc2); // xnss1 & xnss2 are dictionaries based on a unique key Dictionary xnss1 = new Dictionary(); Dictionary xnss2 = new Dictionary(); foreach (XmlNode xc1 in xns1.Values) xnss1.Add(GetKey(xc1), xc1); foreach (XmlNode xc2 in xns2.Values) if (xnss1.ContainsKey(GetKey(xc2))) Compare(xnss1[GetKey(xc2)], xc2, path + "/" + GetKey(xc2)); else xnss2.Add(GetKey(xc2), xc2); // element differences, if counts are different. xns1 elements are not found in xns2 and xns2 elements are not found in xns1 if (xns1.Count == 0 && xns2.Count == 0) return; xns1 = new Dictionary(); // child nodes, key = 'OuterXml', value = node itself xns2 = new Dictionary(); // xns1 starts out with all child nodes of xn1 foreach (XmlNode xc1 in xn1.ChildNodes) { xns1.Add(xc1.OuterXml+GetKey(xc1), xc1); } // xns1 - remove any matching child nodes from xns2 // xns2 - has nodes that are not in xns1 foreach (XmlNode xc2 in xn2.ChildNodes) if (xns1.ContainsKey(xc2.OuterXml + GetKey(xc2))) xns1.Remove(xc2.OuterXml + GetKey(xc2)); else xns2.Add(xc2.OuterXml + GetKey(xc2), xc2); // xnss1 & xnss2 are dictionaries based on a unique key xnss1 = new Dictionary(); xnss2 = new Dictionary(); foreach (XmlNode xc1 in xns1.Values) xnss1.Add(GetKey(xc1), xc1); foreach (XmlNode xc2 in xns2.Values) if (xnss1.ContainsKey(GetKey(xc2))) Compare(xnss1[GetKey(xc2)], xc2, path + "/" + GetKey(xc2)); else xnss2.Add(GetKey(xc2), xc2); // element differences, if counts are different. xns1 elements are not found in xns2 and xns2 elements are not found in xns1 if (xns1.Count == 0 && xns2.Count == 0) return; Console.WriteLine(" {0} {1} {2}", path + "/" + xn1.Name, xns1.Count, xns2.Count); foreach (string key in xnss1.Keys) { if (xnss1[key].Attributes.Count > 0 || xnss1[key].ChildNodes.Count > 0) { XmlNode xnr = MakeXPathFormat(path); if (xnr != null) { XmlNode cloned = xnss1[key].CloneNode(true); XmlNode importNode = ResultsDoc.ImportNode(cloned, true); XmlNode resnd = xnr.AppendChild(importNode); XmlAttribute xKey = ResultsDoc.CreateAttribute("Mode"); xKey.Value = "Deleted"; resnd.Attributes.Append(xKey); xKey = ResultsDoc.CreateAttribute("OldKey"); xKey.Value = GetKey(xnss1[key]); resnd.Attributes.Append(xKey); xnr.AppendChild(resnd); if (resnd.ChildNodes.Count > 0) { foreach (XmlNode cxn in resnd.ChildNodes) if (cxn.Name == "Layout") SuffixAttributes("Old", cxn); } } } xnss1[key].ParentNode.RemoveChild(xnss1[key]); } foreach (string key in xnss2.Keys) { if (xnss2[key].Attributes.Count > 0 || xnss2[key].ChildNodes.Count > 0) { XmlNode xnr = MakeXPathFormat(path); if (xnr != null) { XmlNode cloned = xnss2[key].CloneNode(true); XmlNode importNode = ResultsDoc.ImportNode(cloned, true); XmlNode resnd = xnr.AppendChild(importNode); // if this has subnodes, add the 'Inserted' mode on them, otherwise, out it on this level if (resnd.ChildNodes.Count > 0) { foreach (XmlNode cxn in resnd.ChildNodes) { if (cxn.Name=="Layout") SuffixAttributes("New", cxn); XmlAttribute xKey = ResultsDoc.CreateAttribute("Mode"); xKey.Value = "Inserted"; if (cxn is XmlText) resnd.Attributes.Append(xKey); else cxn.Attributes.Append(xKey); // crashing here on a flag difference - trying to append this attribute to 'false'. } } else { XmlAttribute xKey = ResultsDoc.CreateAttribute("Mode"); xKey.Value = "Inserted"; resnd.Attributes.Append(xKey); } XmlAttribute xKey1 = ResultsDoc.CreateAttribute("NewKey"); xKey1.Value = GetKey(xnss2[key]); resnd.Attributes.Append(xKey1); xnr.AppendChild(resnd); } } xnss2[key].ParentNode.RemoveChild(xnss2[key]); } } private void SuffixAttributes(string suffix, XmlNode resnd) { Dictionary renameList = new Dictionary(); foreach (XmlAttribute xa in resnd.Attributes) renameList.Add(xa.Name, xa.Value); foreach (string key in renameList.Keys) { resnd.Attributes.RemoveNamedItem(key); XmlAttribute xKey1 = ResultsDoc.CreateAttribute(key+suffix); xKey1.Value = renameList[key]; resnd.Attributes.Append(xKey1); } } private Dictionary _AllKeys; public Dictionary AllKeys { get { if (_AllKeys == null) _AllKeys = new Dictionary(); return _AllKeys; } set { _AllKeys = value; } } public string GetKey(XmlNode xn) { string key = GetKey1(xn); if (AllKeys.ContainsKey(xn)) return AllKeys[xn]; AllKeys.Add(xn, key); return key; } // Get unique key, key is a string representing either the name itself or a combination of element name/attribute to make it unique public string GetKey1(XmlNode xn) { if (xn.Attributes == null) return xn.Name; XmlAttribute xi = xn.Attributes.GetNamedItem("Index") as XmlAttribute; if (xi != null) { XmlAttribute xa = xn.Attributes.GetNamedItem("Name") as XmlAttribute; if (xa != null) return string.Format("{0}[{1}]", xn.Name, xa.Value); return string.Format("{0}[{1}]", xn.Name, xi.Value); } XmlAttribute xt = xn.Attributes.GetNamedItem("Token") as XmlAttribute; if(xt != null) return string.Format("{0}[{1}]", xn.Name, xt.Value); XmlAttribute xw = xn.Attributes.GetNamedItem("ReplaceWord") as XmlAttribute; if(xw != null) return string.Format("{0}[{1}]", xn.Name, xw.Value); return xn.Name; } static private XmlNode makeXPath(XmlDocument doc, string xpath) { xpath = xpath.Replace(@"DocStyle[", @"DocStyle[@Name='"); xpath = xpath.Replace(@"]", "']"); return makeXPath(doc, doc as XmlNode, xpath); } static private XmlNode makeXPath(XmlDocument doc, XmlNode parent, string xpath) { if (xpath.Contains("DocStyle")) Console.WriteLine("here"); // grab the next node name in the xpath; or return parent if empty string[] partsOfXPath = xpath.Trim('/').Split('/'); string nextNodeInXPath = partsOfXPath.First(); if (string.IsNullOrEmpty(nextNodeInXPath)) return parent; XmlNode node = parent.SelectSingleNode(nextNodeInXPath); if (node == null) { if (nextNodeInXPath.Contains("@")) // element with an attribute, create both { // make element string elename = nextNodeInXPath.Substring(0, nextNodeInXPath.IndexOf("@") - 1); node = parent.AppendChild(doc.CreateElement(elename)); // make attribute int indx = nextNodeInXPath.IndexOf("@")+1; string name = nextNodeInXPath.Substring(indx, nextNodeInXPath.IndexOf("=",indx)-indx); XmlAttribute xKeyd = doc.CreateAttribute(name); indx = nextNodeInXPath.IndexOf("='",indx)+2; string value = nextNodeInXPath.Substring(indx,nextNodeInXPath.IndexOf("'", indx) - indx); xKeyd.Value = nextNodeInXPath.Substring(indx,nextNodeInXPath.IndexOf("'",indx)-indx); node.Attributes.Append(xKeyd); } else node = parent.AppendChild(doc.CreateElement(nextNodeInXPath)); } // rejoin the remainder of the array as an xpath expression and recurse string rest = String.Join("/", partsOfXPath.Skip(1).ToArray()); return makeXPath(doc, node, rest); } private XmlNode MakeXPathFormat(string xpath) { // first find or make, if not found, the XmlElement within FormatData or DocStyles Console.WriteLine("path = {0}", xpath); return makeXPath(ResultsDoc, xpath); } #region Attributes private void Compare(XmlAttributeCollection atts1, XmlAttributeCollection atts2, string path, XmlNode atts1Par, XmlNode atts2Par) { // go through attributes in first xml document, see if they exist in the 2nd xml document & are identical attribute foreach (XmlAttribute xa1 in atts1) { XmlAttribute xa2 = atts2.GetNamedItem(xa1.Name) as XmlAttribute; if (xa2 == null) { XmlNode xnr = MakeXPathFormat(path); if (xnr != null) { XmlAttribute xKey = ResultsDoc.CreateAttribute(xa1.Name+"Old"); xKey.Value = xa1.Value; xnr.Attributes.Append(xKey); xKey = ResultsDoc.CreateAttribute("OldKey"); xKey.Value = GetKey(atts1Par); if (xKey.Value != null) xnr.Attributes.Append(xKey); } } else if (xa2.Value != xa1.Value) { XmlNode xnr = MakeXPathFormat(path); if (xnr != null) { XmlAttribute xKey = ResultsDoc.CreateAttribute(xa1.Name + "Old"); xKey.Value = xa1.Value; xnr.Attributes.Append(xKey); XmlAttribute xKey2 = ResultsDoc.CreateAttribute(xa2.Name + "New"); xKey2.Value = xa2.Value; xnr.Attributes.Append(xKey2); xKey = ResultsDoc.CreateAttribute("OldKey"); xKey.Value = GetKey(atts1Par); if (xKey.Value != null) xnr.Attributes.Append(xKey); xKey = ResultsDoc.CreateAttribute("NewKey"); xKey.Value = GetKey(atts2Par); if (xKey.Value != null) xnr.Attributes.Append(xKey); } } } // go through attributes in 2nd xml document to see if they exist in the first xml document & are identical foreach (XmlAttribute xa2 in atts2) { XmlAttribute xa1 = atts1.GetNamedItem(xa2.Name) as XmlAttribute; if (xa1 == null) { XmlNode xnr = MakeXPathFormat(path); if (xnr != null) { XmlAttribute xKey = ResultsDoc.CreateAttribute(xa2.Name+"New"); xKey.Value = xa2.Value; xnr.Attributes.Append(xKey); xKey = ResultsDoc.CreateAttribute("NewKey"); xKey.Value = GetKey(atts2Par); if (xKey.Value != null) xnr.Attributes.Append(xKey); } } } } #endregion } }