private void Patch(ref XmlNode sourceNode, XmlDocument diffDoc) { var documentElement = diffDoc.DocumentElement; if (documentElement.LocalName != "xmldiff" || documentElement.NamespaceURI != "http://schemas.microsoft.com/xmltools/2002/xmldiff") { XmlPatchError.Error("Invalid XDL diffgram. Expecting xd:xmldiff as a root element with namespace URI 'http://schemas.microsoft.com/xmltools/2002/xmldiff'."); } XmlNamedNodeMap attributes = documentElement.Attributes; var namedItem1 = (XmlAttribute)attributes.GetNamedItem("srcDocHash"); if (namedItem1 == null) { XmlPatchError.Error("Invalid XDL diffgram. Missing srcDocHash attribute on the xd:xmldiff element."); } ulong hashValue = 0; try { hashValue = ulong.Parse(namedItem1.Value); } catch { XmlPatchError.Error("Invalid XDL diffgram. The srcDocHash attribute has an invalid value."); } var namedItem2 = (XmlAttribute)attributes.GetNamedItem("options"); if (namedItem2 == null) { XmlPatchError.Error("Invalid XDL diffgram. Missing options attribute on the xd:xmldiff element."); } var options = XmlDiffOptions.None; try { options = XmlDiff.ParseOptions(namedItem2.Value); } catch { XmlPatchError.Error("Invalid XDL diffgram. The options attribute has an invalid value."); } this._ignoreChildOrder = (options & XmlDiffOptions.IgnoreChildOrder) != XmlDiffOptions.None; if (!XmlDiff.VerifySource(sourceNode, hashValue, options)) { XmlPatchError.Error("The XDL diffgram is not applicable to this XML document; the srcDocHash value does not match."); } if (sourceNode.NodeType == XmlNodeType.Document) { var patch = this.CreatePatch(sourceNode, documentElement); var xmlDocument = (XmlDocument)sourceNode; var element = xmlDocument.CreateElement("tempRoot"); XmlNode nextSibling; for (var xmlNode = xmlDocument.FirstChild; xmlNode != null; xmlNode = nextSibling) { nextSibling = xmlNode.NextSibling; if (xmlNode.NodeType != XmlNodeType.XmlDeclaration && xmlNode.NodeType != XmlNodeType.DocumentType) { xmlDocument.RemoveChild(xmlNode); element.AppendChild(xmlNode); } } xmlDocument.AppendChild(element); XmlNode currentPosition = null; patch.Apply(element, ref currentPosition); if (sourceNode.NodeType != XmlNodeType.Document) { return; } xmlDocument.RemoveChild(element); XmlNode firstChild; while ((firstChild = element.FirstChild) != null) { element.RemoveChild(firstChild); xmlDocument.AppendChild(firstChild); } } else if (sourceNode.NodeType == XmlNodeType.DocumentFragment) { var patch = this.CreatePatch(sourceNode, documentElement); XmlNode currentPosition = null; patch.Apply(sourceNode, ref currentPosition); } else { var documentFragment = sourceNode.OwnerDocument.CreateDocumentFragment(); var parentNode = sourceNode.ParentNode; var previousSibling = sourceNode.PreviousSibling; parentNode?.RemoveChild(sourceNode); if (sourceNode.NodeType != XmlNodeType.XmlDeclaration) { documentFragment.AppendChild(sourceNode); } else { documentFragment.InnerXml = sourceNode.OuterXml; } var patch = this.CreatePatch(documentFragment, documentElement); XmlNode currentPosition = null; patch.Apply(documentFragment, ref currentPosition); var childNodes = documentFragment.ChildNodes; if (childNodes.Count != 1) { XmlPatchError.Error("Internal Error. {0} nodes left after patch, expecting 1.", childNodes.Count.ToString()); } sourceNode = childNodes.Item(0); documentFragment.RemoveAll(); parentNode?.InsertAfter(sourceNode, previousSibling); } }
private void Patch(ref XmlNode sourceNode, XmlDocument diffDoc) { XmlElement diffgramEl = diffDoc.DocumentElement; if (diffgramEl.LocalName != "xmldiff" || diffgramEl.NamespaceURI != XmlDiff.NamespaceUri) { XmlPatchError.Error(XmlPatchError.ExpectingDiffgramElement); } XmlNamedNodeMap diffgramAttributes = diffgramEl.Attributes; XmlAttribute srcDocAttr = (XmlAttribute)diffgramAttributes.GetNamedItem("srcDocHash"); if (srcDocAttr == null) { XmlPatchError.Error(XmlPatchError.MissingSrcDocAttribute); } ulong hashValue = 0; try { hashValue = ulong.Parse(srcDocAttr.Value); } catch { XmlPatchError.Error(XmlPatchError.InvalidSrcDocAttribute); } XmlAttribute optionsAttr = (XmlAttribute)diffgramAttributes.GetNamedItem("options"); if (optionsAttr == null) { XmlPatchError.Error(XmlPatchError.MissingOptionsAttribute); } // parse options XmlDiffOptions xmlDiffOptions = XmlDiffOptions.None; try { xmlDiffOptions = XmlDiff.ParseOptions(optionsAttr.Value); } catch { XmlPatchError.Error(XmlPatchError.InvalidOptionsAttribute); } _ignoreChildOrder = ((int)xmlDiffOptions & (int)XmlDiffOptions.IgnoreChildOrder) != 0; // Calculate the hash value of source document and check if it agrees with // of srcDocHash attribute value. if (!XmlDiff.VerifySource(sourceNode, hashValue, xmlDiffOptions)) { XmlPatchError.Error(XmlPatchError.SrcDocMismatch); } // Translate diffgram & Apply patch if (sourceNode.NodeType == XmlNodeType.Document) { Patch patch = CreatePatch(sourceNode, diffgramEl); // create temporary root element and move all document children under it XmlDocument sourceDoc = (XmlDocument)sourceNode; XmlElement tempRoot = sourceDoc.CreateElement("tempRoot"); XmlNode child = sourceDoc.FirstChild; while (child != null) { XmlNode tmpChild = child.NextSibling; if (child.NodeType != XmlNodeType.XmlDeclaration && child.NodeType != XmlNodeType.DocumentType) { sourceDoc.RemoveChild(child); tempRoot.AppendChild(child); } child = tmpChild; } sourceDoc.AppendChild(tempRoot); // Apply patch XmlNode temp = null; patch.Apply(tempRoot, ref temp); // remove the temporary root element if (sourceNode.NodeType == XmlNodeType.Document) { sourceDoc.RemoveChild(tempRoot); Debug.Assert(tempRoot.Attributes.Count == 0); while ((child = tempRoot.FirstChild) != null) { tempRoot.RemoveChild(child); sourceDoc.AppendChild(child); } } } else if (sourceNode.NodeType == XmlNodeType.DocumentFragment) { Patch patch = CreatePatch(sourceNode, diffgramEl); XmlNode temp = null; patch.Apply(sourceNode, ref temp); } else { // create fragment with sourceNode as its only child XmlDocumentFragment fragment = sourceNode.OwnerDocument.CreateDocumentFragment(); XmlNode previousSourceParent = sourceNode.ParentNode; XmlNode previousSourceSibbling = sourceNode.PreviousSibling; if (previousSourceParent != null) { previousSourceParent.RemoveChild(sourceNode); } if (sourceNode.NodeType != XmlNodeType.XmlDeclaration) { fragment.AppendChild(sourceNode); } else { fragment.InnerXml = sourceNode.OuterXml; } Patch patch = CreatePatch(fragment, diffgramEl); XmlNode temp = null; patch.Apply(fragment, ref temp); XmlNodeList childNodes = fragment.ChildNodes; if (childNodes.Count != 1) { XmlPatchError.Error(XmlPatchError.InternalErrorMoreThanOneNodeLeft, childNodes.Count.ToString()); } sourceNode = childNodes.Item(0); fragment.RemoveAll(); if (previousSourceParent != null) { previousSourceParent.InsertAfter(sourceNode, previousSourceSibbling); } } }