internal override void AddNode(XmlNode node) { if (this._node != null) { XmlPatchError.Error("Internal Error. XmlDiffPathSingleNodeList can contain one node only."); } this._node = node; }
internal override void AddNode(XmlNode node) { Debug.Assert(_node == null); if (_node != null) { XmlPatchError.Error(XmlPatchError.InternalErrorMoreThanOneNodeInList); } _node = node; }
private void CreatePatchForChildren( XmlNode sourceParent, XmlElement diffgramParent, XmlPatchParentOperation patchParent) { XmlPatchOperation child = null; var xmlNode1 = diffgramParent.FirstChild; while (xmlNode1 != null) { if (xmlNode1.NodeType != XmlNodeType.Element) { xmlNode1 = xmlNode1.NextSibling; } else { var diffgramParent1 = (XmlElement)xmlNode1; XmlNodeList xmlNodeList = null; var attribute1 = diffgramParent1.GetAttribute("match"); if (attribute1 != string.Empty) { xmlNodeList = PathDescriptorParser.SelectNodes(this._sourceRootNode, sourceParent, attribute1); if (xmlNodeList.Count == 0) { XmlPatchError.Error("Invalid XDL diffgram. No node matches the path descriptor '{0}'.", attribute1); } } XmlPatchOperation newChild = null; switch (diffgramParent1.LocalName) { case "node": if (xmlNodeList.Count != 1) { XmlPatchError.Error("Invalid XDL diffgram; more than one node matches the '{0}' path descriptor on the xd:node or xd:change element.", attribute1); } var xmlNode2 = xmlNodeList.Item(0); if (this._sourceRootNode.NodeType != XmlNodeType.Document || xmlNode2.NodeType != XmlNodeType.XmlDeclaration && xmlNode2.NodeType != XmlNodeType.DocumentType) { newChild = new PatchSetPosition(xmlNode2); this.CreatePatchForChildren(xmlNode2, diffgramParent1, (XmlPatchParentOperation)newChild); break; } break; case "add": if (attribute1 != string.Empty) { var bSubtree = diffgramParent1.GetAttribute("subtree") != "no"; newChild = new PatchCopy(xmlNodeList, bSubtree); if (!bSubtree) { this.CreatePatchForChildren(sourceParent, diffgramParent1, (XmlPatchParentOperation)newChild); break; } break; } var attribute2 = diffgramParent1.GetAttribute("type"); if (attribute2 != string.Empty) { var nodeType = (XmlNodeType)int.Parse(attribute2); var flag = nodeType == XmlNodeType.Element; if (nodeType != XmlNodeType.DocumentType) { newChild = new PatchAddNode(nodeType, diffgramParent1.GetAttribute("name"), diffgramParent1.GetAttribute("ns"), diffgramParent1.GetAttribute("prefix"), flag ? string.Empty : diffgramParent1.InnerText, this._ignoreChildOrder); if (flag) { this.CreatePatchForChildren(sourceParent, diffgramParent1, (XmlPatchParentOperation)newChild); break; } break; } newChild = new PatchAddNode(nodeType, diffgramParent1.GetAttribute("name"), diffgramParent1.GetAttribute("systemId"), diffgramParent1.GetAttribute("publicId"), diffgramParent1.InnerText, this._ignoreChildOrder); break; } newChild = new PatchAddXmlFragment(diffgramParent1.ChildNodes); break; case "remove": var bSubtree1 = diffgramParent1.GetAttribute("subtree") != "no"; newChild = new PatchRemove(xmlNodeList, bSubtree1); if (!bSubtree1) { this.CreatePatchForChildren(xmlNodeList.Item(0), diffgramParent1, (XmlPatchParentOperation)newChild); break; } break; case "change": if (xmlNodeList.Count != 1) { XmlPatchError.Error("Invalid XDL diffgram; more than one node matches the '{0}' path descriptor on the xd:node or xd:change element.", attribute1); } var xmlNode3 = xmlNodeList.Item(0); newChild = xmlNode3.NodeType == XmlNodeType.DocumentType ? new PatchChange(xmlNode3, diffgramParent1.HasAttribute("name") ? diffgramParent1.GetAttribute("name") : null, diffgramParent1.HasAttribute("systemId") ? diffgramParent1.GetAttribute("systemId") : null, diffgramParent1.HasAttribute("publicId") ? diffgramParent1.GetAttribute("publicId") : null, diffgramParent1.IsEmpty ? null : diffgramParent1) : new PatchChange(xmlNode3, diffgramParent1.HasAttribute("name") ? diffgramParent1.GetAttribute("name") : null, diffgramParent1.HasAttribute("ns") ? diffgramParent1.GetAttribute("ns") : null, diffgramParent1.HasAttribute("prefix") ? diffgramParent1.GetAttribute("prefix") : null, xmlNode3.NodeType == XmlNodeType.Element ? null : diffgramParent1); if (xmlNode3.NodeType == XmlNodeType.Element) { this.CreatePatchForChildren(xmlNode3, diffgramParent1, (XmlPatchParentOperation)newChild); break; } break; case "descriptor": return; } if (newChild != null) { patchParent.InsertChildAfter(child, newChild); child = newChild; } xmlNode1 = xmlNode1.NextSibling; } } }
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 static void OnNoMatchingNode(string path) { XmlPatchError.Error("Invalid XDL diffgram. No node matches the path descriptor '{0}'.", path); }
internal static void Error(string message, string arg1) { XmlPatchError.Error(string.Format(message, (object)arg1)); }
private static void OnInvalidExpression(string path) { XmlPatchError.Error("Invalid XDL diffgram. '{0}' is an invalid path descriptor.", path); }
private static void OnNoMatchingNode(string path) { XmlPatchError.Error(XmlPatchError.NoMatchingNode, path); }
private static void OnInvalidExpression(string path) { XmlPatchError.Error(XmlPatchError.InvalidPathDescriptor, path); }
private void CreatePatchForChildren(XmlNode sourceParent, XmlElement diffgramParent, XmlPatchParentOperation patchParent) { Debug.Assert(sourceParent != null); Debug.Assert(diffgramParent != null); Debug.Assert(patchParent != null); XmlPatchOperation lastPatchOp = null; XmlNode node = diffgramParent.FirstChild; while (node != null) { if (node.NodeType != XmlNodeType.Element) { node = node.NextSibling; continue; } XmlElement diffOp = (XmlElement)node; XmlNodeList matchNodes = null; string matchAttr = diffOp.GetAttribute("match"); if (matchAttr != string.Empty) { matchNodes = PathDescriptorParser.SelectNodes(_sourceRootNode, sourceParent, matchAttr); if (matchNodes.Count == 0) { XmlPatchError.Error(XmlPatchError.NoMatchingNode, matchAttr); } } XmlPatchOperation patchOp = null; switch (diffOp.LocalName) { case "node": { Debug.Assert(matchAttr != string.Empty); if (matchNodes.Count != 1) { XmlPatchError.Error(XmlPatchError.MoreThanOneNodeMatched, matchAttr); } XmlNode matchNode = matchNodes.Item(0); if (_sourceRootNode.NodeType != XmlNodeType.Document || (matchNode.NodeType != XmlNodeType.XmlDeclaration && matchNode.NodeType != XmlNodeType.DocumentType)) { patchOp = new PatchSetPosition(matchNode); CreatePatchForChildren(matchNode, diffOp, (XmlPatchParentOperation)patchOp); } break; } case "add": { // copy node/subtree if (matchAttr != string.Empty) { bool bSubtree = diffOp.GetAttribute("subtree") != "no"; patchOp = new PatchCopy(matchNodes, bSubtree); if (!bSubtree) { CreatePatchForChildren(sourceParent, diffOp, (XmlPatchParentOperation)patchOp); } } else { string type = diffOp.GetAttribute("type"); // add single node if (type != string.Empty) { XmlNodeType nodeType = (XmlNodeType)int.Parse(type); bool bElement = (nodeType == XmlNodeType.Element); if (nodeType != XmlNodeType.DocumentType) { patchOp = new PatchAddNode(nodeType, diffOp.GetAttribute("name"), diffOp.GetAttribute("ns"), diffOp.GetAttribute("prefix"), bElement ? string.Empty : diffOp.InnerText, _ignoreChildOrder); if (bElement) { CreatePatchForChildren(sourceParent, diffOp, (XmlPatchParentOperation)patchOp); } } else { patchOp = new PatchAddNode(nodeType, diffOp.GetAttribute("name"), diffOp.GetAttribute("systemId"), diffOp.GetAttribute("publicId"), diffOp.InnerText, _ignoreChildOrder); } } // add blob else { Debug.Assert(diffOp.ChildNodes.Count > 0); patchOp = new PatchAddXmlFragment(diffOp.ChildNodes); } } break; } case "remove": { Debug.Assert(matchAttr != string.Empty); bool bSubtree = diffOp.GetAttribute("subtree") != "no"; patchOp = new PatchRemove(matchNodes, bSubtree); if (!bSubtree) { Debug.Assert(matchNodes.Count == 1); CreatePatchForChildren(matchNodes.Item(0), diffOp, (XmlPatchParentOperation)patchOp); } break; } case "change": { Debug.Assert(matchAttr != string.Empty); if (matchNodes.Count != 1) { XmlPatchError.Error(XmlPatchError.MoreThanOneNodeMatched, matchAttr); } XmlNode matchNode = matchNodes.Item(0); if (matchNode.NodeType != XmlNodeType.DocumentType) { patchOp = new PatchChange(matchNode, diffOp.HasAttribute("name") ? diffOp.GetAttribute("name") : null, diffOp.HasAttribute("ns") ? diffOp.GetAttribute("ns") : null, diffOp.HasAttribute("prefix") ? diffOp.GetAttribute("prefix") : null, (matchNode.NodeType == XmlNodeType.Element) ? null : diffOp); } else { patchOp = new PatchChange(matchNode, diffOp.HasAttribute("name") ? diffOp.GetAttribute("name") : null, diffOp.HasAttribute("systemId") ? diffOp.GetAttribute("systemId") : null, diffOp.HasAttribute("publicId") ? diffOp.GetAttribute("publicId") : null, diffOp.IsEmpty ? null : diffOp); } if (matchNode.NodeType == XmlNodeType.Element) { CreatePatchForChildren(matchNode, diffOp, (XmlPatchParentOperation)patchOp); } break; } case "descriptor": return; default: Debug.Assert(false, "Invalid element in the XDL diffgram ."); break; } if (patchOp != null) { patchParent.InsertChildAfter(lastPatchOp, patchOp); lastPatchOp = patchOp; } node = node.NextSibling; } }
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); } } }