internal DiffgramRemoveSubtrees(XmlDiffNode sourceNode, ulong operationID, bool bSorted) : base(operationID) { this._firstSourceNode = sourceNode; this._lastSourceNode = sourceNode; this._bSorted = bSorted; }
internal XmlDiffNode(int position) { this._parent = (XmlDiffParentNode)null; this._nextSibling = (XmlDiffNode)null; this._position = position; this._bExpanded = false; }
private void Sort() { XmlDiffNode prevSibling = null; XmlDiff.SortNodesByPosition(ref _firstSourceNode, ref _lastSourceNode, ref prevSibling); _bSorted = true; }
// Constructor internal XmlDiffShrankNode(XmlDiffNode firstNode, XmlDiffNode lastNode) : base(-1) { Debug.Assert(firstNode != null); Debug.Assert(lastNode != null); Debug.Assert(firstNode.Position <= lastNode.Position); Debug.Assert(firstNode.NodeType != XmlDiffNodeType.Attribute || (firstNode.NodeType == XmlDiffNodeType.Attribute && firstNode == lastNode)); _firstNode = firstNode; _lastNode = lastNode; _matchingShrankNode = null; // hash value XmlDiffNode curNode = firstNode; for (;;) { _hashValue += (_hashValue << 7) + curNode.HashValue; if (curNode == lastNode) { break; } curNode = curNode._nextSibling; } _localAddress = DiffgramOperation.GetRelativeAddressOfNodeset(_firstNode, _lastNode); }
// Loads the document from XmlReader internal virtual void Load(XmlReader reader, XmlHash xmlHash) { if (_bLoaded) { throw new InvalidOperationException("The document already contains data and should not be used again."); } try { _curLastChild = null; _xmlHash = xmlHash; LoadChildNodes(this, reader, false); ComputeHashValue(_xmlHash); _bLoaded = true; #if DEBUG if (XmlDiff.T_LoadedDoc.Enabled) { Trace.Write("\nLoaded document " + reader.BaseURI + ": \n"); Dump(); } #endif } finally { _xmlHash = null; } }
// Methods internal virtual void InsertChildNodeAfter(XmlDiffNode childNode, XmlDiffNode newChildNode) { Debug.Assert(newChildNode != null); Debug.Assert(!(newChildNode is XmlDiffAttributeOrNamespace)); Debug.Assert(childNode == null || childNode._parent == this); #if DEBUG if (newChildNode.NodeType == XmlDiffNodeType.Attribute) { Debug.Assert(childNode == null || childNode.NodeType == XmlDiffNodeType.Attribute || childNode.NodeType == XmlDiffNodeType.Namespace); } #endif newChildNode._parent = this; if (childNode == null) { newChildNode._nextSibling = _firstChildNode; _firstChildNode = newChildNode; } else { newChildNode._nextSibling = childNode._nextSibling; childNode._nextSibling = newChildNode; } Debug.Assert(newChildNode.NodesCount > 0); _nodesCount += newChildNode.NodesCount; if (newChildNode.NodeType != XmlDiffNodeType.Element && !(newChildNode is XmlDiffAttributeOrNamespace)) { _elementChildrenOnly = false; } }
// compares the node to another one and returns the xmldiff operation for changing this node to the other internal override XmlDiffOperation GetDiffOperation(XmlDiffNode changedNode, XmlDiff xmlDiff) { Debug.Assert(changedNode != null); if (changedNode.NodeType != XmlDiffNodeType.ProcessingInstruction) { return(XmlDiffOperation.Undefined); } XmlDiffPI changedPI = (XmlDiffPI)changedNode; if (Name == changedPI.Name) { if (Value == changedPI.Value) { return(XmlDiffOperation.Match); } else { return(XmlDiffOperation.ChangePI); } } else { if (Value == changedPI.Value) { return(XmlDiffOperation.ChangePI); } else { return(XmlDiffOperation.Undefined); } } }
// internal bool _nodeOrDescendantMatches = false; // Constructors internal XmlDiffNode(int position) { _parent = null; _nextSibling = null; _position = position; _bExpanded = false; }
private XmlDiffNode HTFindAndRemoveMatchingNode( Hashtable hashtable, XmlDiffNodeListHead nodeListHead, XmlDiffNode nodeToMatch) { var first = nodeListHead._first; var node = first._node; if (this.IdenticalSubtrees(node, nodeToMatch)) { if (first._next == null) { hashtable.Remove(node.HashValue); } else { nodeListHead._first = first._next; } return(node); } while (first._next != null) { if (this.IdenticalSubtrees(first._node, nodeToMatch)) { first._next = first._next._next; if (first._next == null) { nodeListHead._last = first; } return(node); } } return(null); }
// internal bool _nodeOrDescendantMatches = false; // Constructors internal XmlDiffNode( int position ) { _parent = null; _nextSibling = null; _position = position; _bExpanded = false; }
internal static void SortNodesByPosition( ref XmlDiffNode firstNode, ref XmlDiffNode lastNode, ref XmlDiffNode firstPreviousSibbling) { var parent = firstNode._parent; if (firstPreviousSibbling == null && firstNode != parent._firstChildNode) { firstPreviousSibbling = parent._firstChildNode; while (firstPreviousSibbling._nextSibling != firstNode) { firstPreviousSibbling = firstPreviousSibbling._nextSibling; } } var nextSibling = lastNode._nextSibling; lastNode._nextSibling = null; var count = 0; for (var xmlDiffNode = firstNode; xmlDiffNode != null; xmlDiffNode = xmlDiffNode._nextSibling) { ++count; } if (count >= 5) { XmlDiff.QuickSortNodes(ref firstNode, ref lastNode, count, firstPreviousSibbling, nextSibling); } else { XmlDiff.SlowSortNodes(ref firstNode, ref lastNode, firstPreviousSibbling, nextSibling); } }
internal override bool IsSameAs(XmlDiffNode node, XmlDiff xmlDiff) { if (node.NodeType != XmlDiffNodeType.Element) { return(false); } var xmlDiffElement = (XmlDiffElement)node; if (this.LocalName != xmlDiffElement.LocalName || !xmlDiff.IgnoreNamespaces && (this.NamespaceURI != xmlDiffElement.NamespaceURI || !xmlDiff.IgnorePrefixes && this.Prefix != xmlDiffElement.Prefix)) { return(false); } var attributeOrNamespace1 = this._attributes; while (attributeOrNamespace1 != null && attributeOrNamespace1.NodeType == XmlDiffNodeType.Namespace) { attributeOrNamespace1 = (XmlDiffAttributeOrNamespace)attributeOrNamespace1._nextSibling; } var attributeOrNamespace2 = this._attributes; while (attributeOrNamespace2 != null && attributeOrNamespace2.NodeType == XmlDiffNodeType.Namespace) { attributeOrNamespace2 = (XmlDiffAttributeOrNamespace)attributeOrNamespace2._nextSibling; } for (; attributeOrNamespace1 != null && attributeOrNamespace2 != null; attributeOrNamespace2 = (XmlDiffAttributeOrNamespace)attributeOrNamespace2._nextSibling) { if (!attributeOrNamespace1.IsSameAs((XmlDiffNode)attributeOrNamespace2, xmlDiff)) { return(false); } attributeOrNamespace1 = (XmlDiffAttributeOrNamespace)attributeOrNamespace1._nextSibling; } return(attributeOrNamespace1 == null && attributeOrNamespace2 == null); }
internal override XmlDiffOperation GetDiffOperation( XmlDiffNode changedNode, XmlDiff xmlDiff) { if (changedNode.NodeType != XmlDiffNodeType.Element) { return(XmlDiffOperation.Undefined); } var xmlDiffElement = (XmlDiffElement)changedNode; var flag = false; if (this.LocalName == xmlDiffElement.LocalName) { if (xmlDiff.IgnoreNamespaces) { flag = true; } else if (this.NamespaceURI == xmlDiffElement.NamespaceURI && (xmlDiff.IgnorePrefixes || this.Prefix == xmlDiffElement.Prefix)) { flag = true; } } if ((long)xmlDiffElement._allAttributesHash == (long)this._allAttributesHash) { return(!flag ? XmlDiffOperation.ChangeElementName : XmlDiffOperation.Match); } var num = ((long)xmlDiffElement._attributesHashAH == (long)this._attributesHashAH ? 0 : 1) + ((long)xmlDiffElement._attributesHashIQ == (long)this._attributesHashIQ ? 0 : 1) + ((long)xmlDiffElement._attributesHashRZ == (long)this._attributesHashRZ ? 0 : 1); return(flag ? (XmlDiffOperation)(3 + num) : (XmlDiffOperation)(6 + num)); }
private void HTRemoveAncestors(Hashtable hashtable, XmlDiffNode node) { for (var parent = (XmlDiffNode)node._parent; parent != null && this.HTRemoveNode(hashtable, parent); parent = parent._parent) { parent._bSomeDescendantMatches = true; } }
// compares the node to another one and returns the xmldiff operation for changing this node to the other internal override XmlDiffOperation GetDiffOperation(XmlDiffNode changedNode, XmlDiff xmlDiff) { Debug.Assert(changedNode != null); if (NodeType != changedNode.NodeType) { return(XmlDiffOperation.Undefined); } XmlDiffCharData changedCD = changedNode as XmlDiffCharData; if (changedCD == null) { return(XmlDiffOperation.Undefined); } if (Value == changedCD.Value) { return(XmlDiffOperation.Match); } else { return(XmlDiffOperation.ChangeCharacterData); } }
// Loads the document from XmlReader internal virtual void Load( XmlReader reader, XmlHash xmlHash ) { if ( _bLoaded ) throw new InvalidOperationException( "The document already contains data and should not be used again." ); try { _curLastChild = null; _xmlHash = xmlHash; LoadChildNodes( this, reader, false ); ComputeHashValue( _xmlHash ); _bLoaded = true; #if DEBUG if ( XmlDiff.T_LoadedDoc.Enabled ) { Trace.Write( "\nLoaded document " + reader.BaseURI + ": \n" ); Dump(); } #endif } finally { _xmlHash = null; } }
// Constructor internal DiffgramRemoveSubtrees(XmlDiffNode sourceNode, ulong operationID, bool bSorted) : base(operationID) { Debug.Assert(sourceNode != null); _firstSourceNode = sourceNode; _lastSourceNode = sourceNode; _bSorted = bSorted; }
internal static int OrderChildren(XmlDiffNode node1, XmlDiffNode node2) { var nodeType1 = (int) node1.NodeType; var nodeType2 = (int) node2.NodeType; if (nodeType1 < nodeType2) return -1; if (nodeType2 < nodeType1) return 1; switch (nodeType1) { case 1: return XmlDiffDocument.OrderElements(node1 as XmlDiffElement, node2 as XmlDiffElement); case 2: case 100: return 0; case 5: return XmlDiffDocument.OrderERs(node1 as XmlDiffER, node2 as XmlDiffER); case 7: return XmlDiffDocument.OrderPIs(node1 as XmlDiffPI, node2 as XmlDiffPI); case 101: if (((XmlDiffShrankNode) node1).MatchingShrankNode == ((XmlDiffShrankNode) node2).MatchingShrankNode) return 0; return node1.HashValue >= node2.HashValue ? 1 : -1; default: return XmlDiffDocument.OrderCharacterData(node1 as XmlDiffCharData, node2 as XmlDiffCharData); } }
private static void QuickSortNodes( ref XmlDiffNode firstNode, ref XmlDiffNode lastNode, int count, XmlDiffNode firstPreviousSibbling, XmlDiffNode lastNextSibling) { var sortArray = new XmlDiffNode[(int)checked ((uint)count)]; var xmlDiffNode = firstNode; var index1 = 0; while (index1 < count) { sortArray[index1] = xmlDiffNode; ++index1; xmlDiffNode = xmlDiffNode._nextSibling; } XmlDiff.QuickSortNodesRecursion(ref sortArray, 0, count - 1); for (var index2 = 0; index2 < count - 1; ++index2) { sortArray[index2]._nextSibling = sortArray[index2 + 1]; } if (firstPreviousSibbling == null) { firstNode._parent._firstChildNode = sortArray[0]; } else { firstPreviousSibbling._nextSibling = sortArray[0]; } sortArray[count - 1]._nextSibling = lastNextSibling; firstNode = sortArray[0]; lastNode = sortArray[count - 1]; }
private void PreprocessNode( XmlDiffNode node, ref XmlDiffNode[] postOrderArray, ref int currentIndex) { if (node.HasChildNodes) { var node1 = node.FirstChildNode; node1._bKeyRoot = false; while (true) { this.PreprocessNode(node1, ref postOrderArray, ref currentIndex); node1 = node1._nextSibling; if (node1 != null) { node1._bKeyRoot = true; } else { break; } } node.Left = node.FirstChildNode.Left; } else { node.Left = currentIndex; node.NodesCount = 1; } postOrderArray[currentIndex++] = node; }
// Methods internal override void WriteTo(XmlWriter xmlWriter, XmlDiff xmlDiff) { if (!_bSorted) { Sort(); } xmlWriter.WriteStartElement(XmlDiff.Prefix, "add", XmlDiff.NamespaceUri); if (_operationID != 0) { xmlWriter.WriteAttributeString("opid", _operationID.ToString()); } // namespaces if (_bNeedNamespaces) { Hashtable definedPrefixes = new Hashtable(); XmlDiffParentNode parent = _firstTargetNode._parent; while (parent != null) { if (parent._bDefinesNamespaces) { XmlDiffElement el = (XmlDiffElement)parent; XmlDiffAttributeOrNamespace curNs = el._attributes; while (curNs != null && curNs.NodeType == XmlDiffNodeType.Namespace) { if (definedPrefixes[curNs.Prefix] == null) { if (curNs.Prefix == string.Empty) { xmlWriter.WriteAttributeString("xmlns", XmlDiff.XmlnsNamespaceUri, curNs.NamespaceURI); } else { xmlWriter.WriteAttributeString("xmlns", curNs.Prefix, XmlDiff.XmlnsNamespaceUri, curNs.NamespaceURI); } definedPrefixes[curNs.Prefix] = curNs.Prefix; } curNs = (XmlDiffAttributeOrNamespace)curNs._nextSibling; } } parent = parent._parent; } } // output nodes XmlDiffNode node = _firstTargetNode; for (;;) { node.WriteTo(xmlWriter); if (node == _lastTargetNode) { break; } node = node._nextSibling; } xmlWriter.WriteEndElement(); }
// compares the node to another one and returns true, if the nodes are identical; // on elements this method ignores namespace declarations internal override bool IsSameAs(XmlDiffNode node, XmlDiff xmlDiff) { // check node type Debug.Assert(node != null); if (node.NodeType != XmlDiffNodeType.Element) { return(false); } XmlDiffElement element = (XmlDiffElement)node; // check element name if (LocalName != element.LocalName) { return(false); } else if (!xmlDiff.IgnoreNamespaces) { if (NamespaceURI != element.NamespaceURI) { return(false); } else if (!xmlDiff.IgnorePrefixes) { if (Prefix != element.Prefix) { return(false); } } } // ignore namespace definitions - should be first in the list of attributes XmlDiffAttributeOrNamespace attr1 = _attributes; while (attr1 != null && attr1.NodeType == XmlDiffNodeType.Namespace) { attr1 = (XmlDiffAttributeOrNamespace)attr1._nextSibling; } XmlDiffAttributeOrNamespace attr2 = _attributes; while (attr2 != null && attr2.NodeType == XmlDiffNodeType.Namespace) { attr2 = (XmlDiffAttributeOrNamespace)attr2._nextSibling; } // check attributes while (attr1 != null && attr2 != null) { if (!attr1.IsSameAs(attr2, xmlDiff)) { return(false); } attr1 = (XmlDiffAttributeOrNamespace)attr1._nextSibling; attr2 = (XmlDiffAttributeOrNamespace)attr2._nextSibling; } return(attr1 == null && attr2 == null); }
internal static string GetRelativeAddressOfInterval(XmlDiffNode firstNode, XmlDiffNode lastNode) { if (firstNode == lastNode) { return(firstNode.GetRelativeAddress()); } return(firstNode._parent._firstChildNode == firstNode && lastNode._nextSibling == null ? "*" : firstNode.Position.ToString() + "-" + lastNode.Position.ToString()); }
internal DiffgramAddSubtrees(XmlDiffNode subtreeRoot, ulong operationID, bool bSorted) : base(operationID) { this._firstTargetNode = subtreeRoot; this._lastTargetNode = subtreeRoot; this._bSorted = bSorted; this._bNeedNamespaces = subtreeRoot.NodeType == XmlDiffNodeType.Element; }
// Constructor internal DiffgramAddSubtrees(XmlDiffNode subtreeRoot, ulong operationID, bool bSorted) : base(operationID) { Debug.Assert(subtreeRoot != null); _firstTargetNode = subtreeRoot; _lastTargetNode = subtreeRoot; _bSorted = bSorted; _bNeedNamespaces = subtreeRoot.NodeType == XmlDiffNodeType.Element; }
// Constructor internal XmlDiffParentNode(int position) : base(position) { _firstChildNode = null; _nodesCount = 1; _elementChildrenOnly = true; _bDefinesNamespaces = false; _hashValue = 0; }
// compares the node to another one and returns the xmldiff operation for changing this node to the other internal override XmlDiffOperation GetDiffOperation(XmlDiffNode changedNode, XmlDiff xmlDiff) { Debug.Assert(changedNode != null); if (changedNode.NodeType != XmlDiffNodeType.Element) { return(XmlDiffOperation.Undefined); } XmlDiffElement changedElement = (XmlDiffElement)changedNode; // name bool bNameMatches = false; if (LocalName == changedElement.LocalName) { if (xmlDiff.IgnoreNamespaces) { bNameMatches = true; } else { if (NamespaceURI == changedElement.NamespaceURI && (xmlDiff.IgnorePrefixes || Prefix == changedElement.Prefix)) { bNameMatches = true; } } } // attributes if (changedElement._allAttributesHash == _allAttributesHash) { return(bNameMatches ? XmlDiffOperation.Match : XmlDiffOperation.ChangeElementName); } int n = (changedElement._attributesHashAH == _attributesHashAH ? 0 : 1) + (changedElement._attributesHashIQ == _attributesHashIQ ? 0 : 1) + (changedElement._attributesHashRZ == _attributesHashRZ ? 0 : 1); Debug.Assert((int)XmlDiffOperation.ChangeElementName + 1 == (int)XmlDiffOperation.ChangeElementAttr1); Debug.Assert((int)XmlDiffOperation.ChangeElementAttr1 + 1 == (int)XmlDiffOperation.ChangeElementAttr2); Debug.Assert((int)XmlDiffOperation.ChangeElementAttr2 + 1 == (int)XmlDiffOperation.ChangeElementAttr3); Debug.Assert((int)XmlDiffOperation.ChangeElementAttr3 + 1 == (int)XmlDiffOperation.ChangeElementNameAndAttr1); Debug.Assert((int)XmlDiffOperation.ChangeElementNameAndAttr1 + 1 == (int)XmlDiffOperation.ChangeElementNameAndAttr2); Debug.Assert((int)XmlDiffOperation.ChangeElementNameAndAttr2 + 1 == (int)XmlDiffOperation.ChangeElementNameAndAttr3); Debug.Assert(n != 0); if (bNameMatches) { return((XmlDiffOperation)(((int)XmlDiffOperation.ChangeElementName) + n)); } else { return((XmlDiffOperation)(((int)XmlDiffOperation.ChangeElementAttr3) + n)); } }
internal bool SetNewLastNode(XmlDiffNode srcNode) { if (this._operationID != 0UL || this._lastSourceNode._nextSibling != srcNode || (!srcNode.CanMerge || !this._firstSourceNode.CanMerge)) { return(false); } this._lastSourceNode = srcNode; return(true); }
private void PreprocessTree(XmlDiffDocument doc, ref XmlDiffNode[] postOrderArray) { postOrderArray = new XmlDiffNode[(int)checked ((uint)unchecked (doc.NodesCount + 1))]; postOrderArray[0] = null; var currentIndex = 1; this.PreprocessNode(doc, ref postOrderArray, ref currentIndex); doc._bKeyRoot = true; }
internal bool MergeAddSubtreeAtEnd(XmlDiffNode subtreeRoot) { Debug.Assert(subtreeRoot.NodeType != XmlDiffNodeType.Attribute); DiffgramAddSubtrees addSubtrees = _lastChildOp as DiffgramAddSubtrees; return(addSubtrees != null && addSubtrees.SetNewLastNode(subtreeRoot)); }
internal DiffgramPosition(XmlDiffNode sourceNode) : base(0UL) { if (sourceNode is XmlDiffShrankNode) { sourceNode = ((XmlDiffShrankNode)sourceNode)._lastNode; } this._sourceNode = sourceNode; }
internal bool MergeRemoveSubtreeAtEnd(XmlDiffNode subtreeRoot) { Debug.Assert(!(subtreeRoot is XmlDiffAttributeOrNamespace)); DiffgramRemoveSubtrees remSubtrees = _lastChildOp as DiffgramRemoveSubtrees; return(remSubtrees != null && remSubtrees.SetNewLastNode(subtreeRoot)); }
// Constructor internal DiffgramAddNode( XmlDiffNode targetNode, ulong operationID ) : base(operationID) { Debug.Assert( targetNode != null ); Debug.Assert( targetNode.NodeType == XmlDiffNodeType.Element || targetNode.NodeType == XmlDiffNodeType.Attribute || targetNode.NodeType == XmlDiffNodeType.Namespace || targetNode.NodeType == XmlDiffNodeType.XmlDeclaration || targetNode.NodeType == XmlDiffNodeType.DocumentType || targetNode.NodeType == XmlDiffNodeType.EntityReference ); _targetNode = targetNode; }
internal static string GetRelativeAddressOfNodeset( XmlDiffNode firstNode, XmlDiffNode lastNode ) { Debug.Assert( !( firstNode is XmlDiffAttributeOrNamespace ) && !( lastNode is XmlDiffAttributeOrNamespace ) ); int prevPosition = -1; bool bInterval = false; StringBuilder sb = new StringBuilder(); XmlDiffNode curNode = firstNode; for (;;) { Debug.Assert( curNode.Position > 0 ); if ( curNode.Position != prevPosition + 1 ) { if ( bInterval ) { sb.Append( prevPosition ); bInterval = false; sb.Append( '|' ); } sb.Append( curNode.Position ); if ( curNode != lastNode ) { if ( curNode._nextSibling.Position == curNode.Position + 1 ) { sb.Append( "-" ); bInterval = true; } else sb.Append( '|' ); } } if ( curNode == lastNode ) break; prevPosition = curNode.Position; curNode = curNode._nextSibling; } if ( bInterval ) sb.Append( lastNode.Position ); return sb.ToString(); }
private void WalkTreeOnAddNode( DiffgramParentOperation diffParent, XmlDiffNode targetNode, XmlDiffNode sourcePositionNode ) { bool bShrankNode = targetNode is XmlDiffShrankNode; if ( _bChildOrderSignificant ) { if ( sourcePositionNode != null ) { diffParent.InsertAtEnd( new DiffgramPosition( sourcePositionNode ) ); } } else { if ( diffParent._firstChildOp == null && diffParent is Diffgram ) { diffParent.InsertAtEnd( new DiffgramPosition( sourcePositionNode ) ); } } if ( targetNode._bSomeDescendantMatches && !bShrankNode ) { DiffgramOperation addOp = GenerateDiffgramAddWhenDescendantMatches( (XmlDiffParentNode)targetNode ); diffParent.InsertAtEnd( addOp ); } else { // shrank node -> output as 'move' operation if ( bShrankNode ) { ulong opid = 0; XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) targetNode; if ( shrankNode.MoveOperationId == 0 ) shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move ); opid = shrankNode.MoveOperationId; diffParent.InsertAtEnd( new DiffgramCopy( shrankNode.MatchingShrankNode, true, opid ) ); } else { switch ( targetNode.NodeType ) { case XmlDiffNodeType.XmlDeclaration: case XmlDiffNodeType.DocumentType: case XmlDiffNodeType.EntityReference: diffParent.InsertAtEnd( new DiffgramAddNode( targetNode, 0 ) ); break; default: if ( !diffParent.MergeAddSubtreeAtEnd( targetNode ) ) { diffParent.InsertAtEnd( new DiffgramAddSubtrees( targetNode, 0, !_xmlDiff.IgnoreChildOrder ) ); } break; } } } }
private void AddNodeToHashTable( Hashtable hashtable, XmlDiffNode node ) { Debug.Assert( hashtable != null ); Debug.Assert( node != null ); Debug.Assert( node.NodeType != XmlDiffNodeType.ShrankNode ); ulong hashValue = node.HashValue; XmlDiffNodeListHead nodeListHead = (XmlDiffNodeListHead) hashtable[ hashValue ]; if ( nodeListHead == null ) { hashtable[ hashValue ] = new XmlDiffNodeListHead( new XmlDiffNodeListMember( node, null ) ); } else { XmlDiffNodeListMember newMember = new XmlDiffNodeListMember( node, null ); nodeListHead._last._next = newMember; nodeListHead._last = newMember; } }
private void PreprocessNode( XmlDiffNode node, ref XmlDiffNode[] postOrderArray, ref int currentIndex ) { // process children if ( node.HasChildNodes ) { Debug.Assert( node.FirstChildNode != null ); #if DEBUG int nodesCount = 0; #endif XmlDiffNode curChild = node.FirstChildNode; curChild._bKeyRoot = false; for (;;) { PreprocessNode( curChild, ref postOrderArray, ref currentIndex ); #if DEBUG nodesCount += curChild.NodesCount; #endif curChild = curChild._nextSibling; // 'key root' node is the root node and each node that has a previous sibling node if ( curChild != null ) curChild._bKeyRoot = true; else break; } // leftist leaf in the subtree rooted at 'node' node.Left = node.FirstChildNode.Left; #if DEBUG Debug.Assert( node.NodesCount == nodesCount + 1 ); #endif } else { // leftist leaf in the subtree rooted at 'node' node.Left = currentIndex; // total number of nodes in the subtree rooted at 'node' node.NodesCount = 1; } #if DEBUG node._index = currentIndex; #endif // put the node in post-order array Debug.Assert( postOrderArray.Length > currentIndex ); postOrderArray[ currentIndex++ ] = node; }
private void WalkTreeOnChangeNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode, XmlDiffNode targetNode, XmlDiffOperation op ) { Debug.Assert( sourceNode.NodeType != XmlDiffNodeType.Element && targetNode.NodeType != XmlDiffNodeType.Element ); DiffgramChangeNode changeOp = new DiffgramChangeNode( sourceNode, targetNode, op, 0 ); if ( sourceNode.HasChildNodes || targetNode.HasChildNodes ) { WalkTreeGenerateDiffgramMatch( changeOp, (XmlDiffParentNode) sourceNode, (XmlDiffParentNode) targetNode ); } diffParent.InsertAtEnd( changeOp ); }
private void HTRemoveDescendants( Hashtable hashtable, XmlDiffNode parent ) { if ( !parent._bExpanded || !parent.HasChildNodes ) return; XmlDiffNode curNode = parent.FirstChildNode; for (;;) { Debug.Assert( curNode != null ); if ( curNode._bExpanded && curNode.HasChildNodes ) { curNode = ((XmlDiffParentNode) curNode)._firstChildNode; continue; } HTRemoveNode( hashtable, curNode ); TryNext: if ( curNode._nextSibling != null ) { curNode = curNode._nextSibling; continue; } else if ( curNode._parent != parent ) { curNode = curNode._parent; goto TryNext; } else { break; } } }
private void PreprocessTree( XmlDiffDocument doc, ref XmlDiffNode[] postOrderArray ) { // allocate the array for post-ordered nodes. // The index 0 is not used; this is to have the consistent indexing of all arrays in the algorithm; postOrderArray = new XmlDiffNode[ doc.NodesCount + 1 ]; postOrderArray[0] = null; // recursivelly process all nodes int index = 1; PreprocessNode( doc, ref postOrderArray, ref index ); // root node is a 'key root' node doc._bKeyRoot = true; Debug.Assert( index - 1 == doc.NodesCount ); }
private XmlDiffNode HTFindAndRemoveMatchingNode( Hashtable hashtable, XmlDiffNodeListHead nodeListHead, XmlDiffNode nodeToMatch ) { Debug.Assert( hashtable != null ); Debug.Assert( nodeListHead != null ); // find matching node in the list XmlDiffNodeListMember nodeList = nodeListHead._first; XmlDiffNode node = nodeList._node; if ( IdenticalSubtrees( node, nodeToMatch ) ) { // remove the node itself if ( nodeList._next == null ) { hashtable.Remove( node.HashValue ); } else { Debug.Assert( nodeListHead._first != nodeListHead._last ); nodeListHead._first = nodeList._next; } return node; } else { while ( nodeList._next != null ) { if ( IdenticalSubtrees( nodeList._node, nodeToMatch ) ) { nodeList._next = nodeList._next._next; if ( nodeList._next == null ) { nodeListHead._last = nodeList; } return node; } } return null; } }
// Shrinks the interval of nodes in one or mode XmlDiffShrankNode instances; // The shrank interval can contain only adjacent nodes => the position of two adjacent nodes differs by 1. private void ShrinkNodeInterval( XmlDiffNode firstSourceNode, XmlDiffNode lastSourceNode, XmlDiffNode firstTargetNode, XmlDiffNode lastTargetNode ) { XmlDiffNode sourcePreviousSibling = null; XmlDiffNode targetPreviousSibling = null; // calculate subtree hash value ulong hashValue = 0; XmlDiffNode curNode = firstSourceNode; for (;;) { hashValue += ( hashValue << 7 ) + curNode.HashValue; if ( curNode == lastSourceNode ) break; curNode = curNode._nextSibling; } #if DEBUG // calculate hash value of the second subtree and make sure they are the same ulong hashValue2 = 0; curNode = firstTargetNode; for (;;) { hashValue2 += ( hashValue2 << 7 ) + curNode.HashValue; if ( curNode == lastTargetNode ) break; curNode = curNode._nextSibling; } Debug.Assert( hashValue == hashValue2 ); #endif // IgnoreChildOrder -> the nodes has been sorted by name/value before comparing. // 'Unsort' the matching interval of nodes (=sort by node position) to // group adjacent nodes that can be shrank. if ( IgnoreChildOrder && firstSourceNode != lastSourceNode ) { Debug.Assert( firstTargetNode != lastTargetNode ); SortNodesByPosition( ref firstSourceNode, ref lastSourceNode, ref sourcePreviousSibling ); SortNodesByPosition( ref firstTargetNode, ref lastTargetNode, ref targetPreviousSibling ); } #if DEBUG Trace.WriteIf( T_SubtreeMatching.Enabled, "Shrinking nodes: " ); XmlDiffNode node = firstSourceNode; for (;;) { Trace.WriteIf( T_SubtreeMatching.Enabled, node.OuterXml ); if ( node == lastSourceNode ) break; node = node._nextSibling; } Trace.WriteIf( T_SubtreeMatching.Enabled, "\n" ); #endif // replace the interval by XmlDiffShrankNode instance XmlDiffShrankNode sourceShrankNode = ReplaceNodeIntervalWithShrankNode( firstSourceNode, lastSourceNode, sourcePreviousSibling, hashValue ); XmlDiffShrankNode targetShrankNode = ReplaceNodeIntervalWithShrankNode( firstTargetNode, lastTargetNode, targetPreviousSibling, hashValue ); sourceShrankNode.MatchingShrankNode = targetShrankNode; targetShrankNode.MatchingShrankNode = sourceShrankNode; }
internal XmlDiffNodeListMember( XmlDiffNode node, XmlDiffNodeListMember next ) { Debug.Assert( node != null ); _node = node; _next = next; }
static private void QuickSortNodes( ref XmlDiffNode firstNode, ref XmlDiffNode lastNode, int count, XmlDiffNode firstPreviousSibbling, XmlDiffNode lastNextSibling ) { Debug.Assert( count >= MininumNodesForQuicksort ); Debug.Assert( MininumNodesForQuicksort >= 2 ); // allocate & fill in the array XmlDiffNode[] sortArray = new XmlDiffNode[ count ]; { XmlDiffNode curNode = firstNode; for ( int i = 0; i < count; i++, curNode = curNode._nextSibling ) { Debug.Assert( curNode != null ); sortArray[i] = curNode; } } // sort QuickSortNodesRecursion( ref sortArray, 0, count - 1 ); // link the nodes for ( int i = 0; i < count - 1; i++ ) sortArray[i]._nextSibling = sortArray[i+1]; if ( firstPreviousSibbling == null ) firstNode._parent._firstChildNode = sortArray[0]; else firstPreviousSibbling._nextSibling = sortArray[0]; sortArray[count-1]._nextSibling = lastNextSibling; // return firstNode = sortArray[0]; lastNode = sortArray[count-1]; }
static private void SlowSortNodes( ref XmlDiffNode firstNode, ref XmlDiffNode lastNode, XmlDiffNode firstPreviousSibbling, XmlDiffNode lastNextSibling ) { Debug.Assert( firstNode != null ); Debug.Assert( lastNode != null ); XmlDiffNode firstSortedNode = firstNode; XmlDiffNode lastSortedNode = firstNode; XmlDiffNode nodeToSort = firstNode._nextSibling; lastSortedNode._nextSibling = null; while ( nodeToSort != null ) { XmlDiffNode curNode = firstSortedNode; if ( nodeToSort.Position < firstSortedNode.Position ) { XmlDiffNode tmpNode = nodeToSort._nextSibling; nodeToSort._nextSibling = firstSortedNode; firstSortedNode = nodeToSort; nodeToSort = tmpNode; } else { while ( curNode._nextSibling != null && nodeToSort.Position > curNode._nextSibling.Position ) curNode = curNode._nextSibling; XmlDiffNode tmpNode = nodeToSort._nextSibling; if ( curNode._nextSibling == null ) lastSortedNode = nodeToSort; nodeToSort._nextSibling = curNode._nextSibling; curNode._nextSibling = nodeToSort; nodeToSort = tmpNode; } } // reconnect the sorted part in the tree if ( firstPreviousSibbling == null ) firstNode._parent._firstChildNode = firstSortedNode; else firstPreviousSibbling._nextSibling = firstSortedNode; lastSortedNode._nextSibling = lastNextSibling; // return firstNode = firstSortedNode; lastNode = lastSortedNode; }
internal static void SortNodesByPosition( ref XmlDiffNode firstNode, ref XmlDiffNode lastNode, ref XmlDiffNode firstPreviousSibbling ) { XmlDiffParentNode parent = firstNode._parent; // find previous sibling node for the first node if ( firstPreviousSibbling == null && firstNode != parent._firstChildNode ) { firstPreviousSibbling = parent._firstChildNode; while ( firstPreviousSibbling._nextSibling != firstNode ) firstPreviousSibbling = firstPreviousSibbling._nextSibling; } // save the next sibling node for the last node XmlDiffNode lastNextSibling = lastNode._nextSibling; lastNode._nextSibling = null; // count the number of nodes to sort int count = 0; XmlDiffNode curNode = firstNode; while ( curNode != null ) { count++; curNode = curNode._nextSibling; } Debug.Assert( count > 0 ); if ( count >= MininumNodesForQuicksort ) QuickSortNodes( ref firstNode, ref lastNode, count, firstPreviousSibbling, lastNextSibling ); else SlowSortNodes( ref firstNode, ref lastNode, firstPreviousSibbling, lastNextSibling ); }
private void RemoveDescendantsFromHashTable( Hashtable hashtable, XmlDiffNode parentNode ) { }
private void PostponedRemoveSubtrees( XmlDiffNode sourceNode, ulong operationID, int startSourceIndex, int endSourceIndex) { Debug.Assert( _bBuildingAddTree ); Debug.Assert( sourceNode != null ); if ( operationID == 0 && _postponedEditScript._firstES != null ) { Debug.Assert( _postponedEditScript._lastES._startSourceIndex > endSourceIndex ); DiffgramRemoveSubtrees remSubtrees = _postponedEditScript._lastES._diffOperation as DiffgramRemoveSubtrees; if ( remSubtrees != null && remSubtrees.SetNewFirstNode( sourceNode ) ) { _postponedEditScript._lastES._startSourceIndex = startSourceIndex; _postponedEditScript._startSourceIndex = startSourceIndex; return; } } PostponedOperation( new DiffgramRemoveSubtrees( sourceNode, operationID, !_xmlDiff.IgnoreChildOrder ), startSourceIndex, endSourceIndex ); }
private void WalkTreeOnMatchNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode, XmlDiffNode targetNode, ref XmlDiffNode needPositionSourceNode ) { if ( sourceNode.HasChildNodes || targetNode.HasChildNodes ) { DiffgramPosition diffMatch = new DiffgramPosition( sourceNode ); WalkTreeGenerateDiffgramMatch( diffMatch, (XmlDiffParentNode)sourceNode, (XmlDiffParentNode)targetNode ); diffParent.InsertAtEnd( diffMatch ); needPositionSourceNode = null; } else { if ( sourceNode.NodeType == XmlDiffNodeType.ShrankNode ) { needPositionSourceNode = ((XmlDiffShrankNode)sourceNode)._lastNode; } else { needPositionSourceNode = sourceNode; } } }
private DiffgramOperation GenerateDiffgramAddWhenDescendantMatches( XmlDiffNode targetParent ) { Debug.Assert( targetParent.HasChildNodes ); Debug.Assert( targetParent._bSomeDescendantMatches ); Debug.Assert( targetParent.NodeType != XmlDiffNodeType.ShrankNode ); DiffgramParentOperation diffOp = new DiffgramAddNode( targetParent, 0 ); if ( targetParent.NodeType == XmlDiffNodeType.Element ) { XmlDiffAttributeOrNamespace attr = ((XmlDiffElement)targetParent)._attributes; while ( attr != null ) { diffOp.InsertAtEnd( new DiffgramAddNode( attr, 0 ) ); attr = (XmlDiffAttributeOrNamespace) attr._nextSibling; } } XmlDiffNode child = ((XmlDiffParentNode)targetParent)._firstChildNode; while ( child != null ) { if ( child.NodeType == XmlDiffNodeType.ShrankNode ) { XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) child; if ( shrankNode.MoveOperationId == 0 ) shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move ); diffOp.InsertAtEnd( new DiffgramCopy( shrankNode.MatchingShrankNode, true, shrankNode.MoveOperationId ) ); } else if ( child.HasChildNodes && child._bSomeDescendantMatches ) { diffOp.InsertAtEnd( GenerateDiffgramAddWhenDescendantMatches( (XmlDiffParentNode)child ) ); } else { if ( !diffOp.MergeAddSubtreeAtEnd( child ) ) diffOp.InsertAtEnd( new DiffgramAddSubtrees( child, 0, !_xmlDiff.IgnoreChildOrder ) ); } child = child._nextSibling; } return diffOp; }
private void WalkTreeOnRemoveNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode ) { bool bShrankNode = sourceNode is XmlDiffShrankNode; if ( sourceNode._bSomeDescendantMatches && !bShrankNode ) { DiffgramOperation removeOp = GenerateDiffgramRemoveWhenDescendantMatches( (XmlDiffParentNode)sourceNode ); diffParent.InsertAtEnd( removeOp ); } else { ulong opid = 0; // shrank node -> output as 'move' operation if ( bShrankNode ) { XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) sourceNode; if ( shrankNode.MoveOperationId == 0 ) shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move ); opid = shrankNode.MoveOperationId; } if ( opid != 0 || !diffParent.MergeRemoveSubtreeAtEnd( sourceNode ) ) { diffParent.InsertAtEnd( new DiffgramRemoveSubtrees( sourceNode, opid, !_xmlDiff.IgnoreChildOrder ) ); } } }
private DiffgramOperation GenerateDiffgramRemoveWhenDescendantMatches( XmlDiffNode sourceParent ) { Debug.Assert( sourceParent._bSomeDescendantMatches ); Debug.Assert( sourceParent.NodeType != XmlDiffNodeType.ShrankNode ); DiffgramParentOperation diffOp = new DiffgramRemoveNode( sourceParent, false, 0 ); XmlDiffNode child = ((XmlDiffParentNode)sourceParent)._firstChildNode; while ( child != null ) { if ( child.NodeType == XmlDiffNodeType.ShrankNode ) { XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) child; if ( shrankNode.MoveOperationId == 0 ) shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move ); diffOp.InsertAtEnd( new DiffgramRemoveSubtrees( child, shrankNode.MoveOperationId, !_xmlDiff.IgnoreChildOrder ) ); } else if ( child.HasChildNodes && child._bSomeDescendantMatches ) { diffOp.InsertAtEnd( GenerateDiffgramRemoveWhenDescendantMatches( (XmlDiffParentNode)child ) ); } else { if ( !diffOp.MergeRemoveSubtreeAtEnd( child ) ) diffOp.InsertAtEnd( new DiffgramRemoveSubtrees( child, 0, !_xmlDiff.IgnoreChildOrder ) ); } child = child._nextSibling; } return diffOp; }
private void HTRemoveAncestors( Hashtable hashtable, XmlDiffNode node ) { XmlDiffNode curAncestorNode = node._parent; while ( curAncestorNode != null ) { if ( !HTRemoveNode( hashtable, curAncestorNode ) ) break; curAncestorNode._bSomeDescendantMatches = true; curAncestorNode = curAncestorNode._parent; } }
// returs true if the two subtrees are identical private bool IdenticalSubtrees( XmlDiffNode node1, XmlDiffNode node2 ) { if ( node1.HashValue != node2.HashValue ) return false; else #if VERIFY_HASH_VALUES return CompareSubtrees( node1, node2 ); #else return true; #endif }
// compares two subtrees and returns true if they are identical private bool CompareSubtrees( XmlDiffNode node1, XmlDiffNode node2 ) { Debug.Assert( node1.NodeType != XmlDiffNodeType.Namespace ); Debug.Assert( node2.NodeType != XmlDiffNodeType.Namespace ); if ( !node1.IsSameAs( node2, this ) ) return false; if ( !node1.HasChildNodes ) return true; XmlDiffNode childNode1 = ((XmlDiffParentNode)node1).FirstChildNode; XmlDiffNode childNode2 = ((XmlDiffParentNode)node2).FirstChildNode; while ( childNode1 != null && childNode2 != null ) { if ( !CompareSubtrees( childNode1, childNode2 )) return false; childNode1 = childNode1._nextSibling; childNode2 = childNode2._nextSibling; } Debug.Assert( childNode1 == null && childNode2 == null ); return ( childNode1 == childNode2 ); }
static private void QuickSortNodesRecursion( ref XmlDiffNode[] sortArray, int firstIndex, int lastIndex ) { Debug.Assert( firstIndex < lastIndex ); int pivotPosition = sortArray[ ( firstIndex + lastIndex ) / 2 ].Position; int i = firstIndex; int j = lastIndex; while ( i < j ) { while ( sortArray[i].Position < pivotPosition ) i++; while ( sortArray[j].Position > pivotPosition ) j--; if ( i < j ) { XmlDiffNode tmpNode = sortArray[i]; sortArray[i] = sortArray[j]; sortArray[j] = tmpNode; i++; j--; } else if ( i == j ) { i++; j--; } } if ( firstIndex < j ) QuickSortNodesRecursion( ref sortArray, firstIndex, j ); if ( i < lastIndex ) QuickSortNodesRecursion( ref sortArray, i, lastIndex ); }
private bool HTRemoveNode( Hashtable hashtable, XmlDiffNode node ) { Debug.Assert( hashtable != null ); Debug.Assert( node != null ); XmlDiffNodeListHead xmlNodeListHead = (XmlDiffNodeListHead) hashtable[ node.HashValue ]; if ( xmlNodeListHead == null ) { return false; } XmlDiffNodeListMember xmlNodeList = xmlNodeListHead._first; if ( xmlNodeList._node == node ) { if ( xmlNodeList._next == null ) { hashtable.Remove( node.HashValue ); } else { Debug.Assert( xmlNodeListHead._first != xmlNodeListHead._last ); xmlNodeListHead._first = xmlNodeList._next; } } else { if ( xmlNodeList._next == null ) { return false; } while ( xmlNodeList._next._node != node ) { xmlNodeList = xmlNodeList._next; if ( xmlNodeList._next == null ) { return false; } } xmlNodeList._next = xmlNodeList._next._next; if ( xmlNodeList._next == null ) { xmlNodeListHead._last = xmlNodeList; } } return true; }
private bool NodeInHashTable( Hashtable hashtable, XmlDiffNode node ) { XmlDiffNodeListHead nodeListHeader = (XmlDiffNodeListHead)hashtable[node.HashValue]; if ( nodeListHeader == null ) { return false; } XmlDiffNodeListMember nodeList = nodeListHeader._first; while ( nodeList != null ) { if ( nodeList._node == node ) { return true; } nodeList = nodeList._next; } return false; }
private XmlDiffShrankNode ReplaceNodeIntervalWithShrankNode( XmlDiffNode firstNode, XmlDiffNode lastNode, XmlDiffNode previousSibling, ulong hashValue ) { XmlDiffShrankNode shrankNode = new XmlDiffShrankNode( firstNode, lastNode, hashValue ); XmlDiffParentNode parent = firstNode._parent; // find previous sibling node if ( previousSibling == null && firstNode != parent._firstChildNode ) { previousSibling = parent._firstChildNode; while ( previousSibling._nextSibling != firstNode ) previousSibling = previousSibling._nextSibling; } // insert shrank node if ( previousSibling == null ) { Debug.Assert( firstNode == parent._firstChildNode ); shrankNode._nextSibling = parent._firstChildNode; parent._firstChildNode = shrankNode; } else { shrankNode._nextSibling = previousSibling._nextSibling; previousSibling._nextSibling = shrankNode; } shrankNode._parent = parent; // remove the node interval & count the total number of nodes XmlDiffNode tmpNode; int totalNodesCount = 0; do { tmpNode = shrankNode._nextSibling; totalNodesCount += tmpNode.NodesCount; shrankNode._nextSibling = shrankNode._nextSibling._nextSibling; } while ( tmpNode != lastNode ); // adjust nodes count Debug.Assert( totalNodesCount > 0 ); if ( totalNodesCount > 1 ) { totalNodesCount--; while ( parent != null ) { parent.NodesCount -= totalNodesCount; parent = parent._parent; } } return shrankNode; }
// compares the node to another one and returns the xmldiff operation for changing this node to the other internal override XmlDiffOperation GetDiffOperation( XmlDiffNode changedNode, XmlDiff xmlDiff ) { if ( changedNode.NodeType != XmlDiffNodeType.Document ) return XmlDiffOperation.Undefined; else return XmlDiffOperation.Match; }