DiffgramGenerator.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:65k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // <copyright file="DiffgramGenerator.cs" company="Microsoft">
  3. //     Copyright (c) Microsoft Corporation.  All rights reserved.
  4. // </copyright>                                                                
  5. //------------------------------------------------------------------------------
  6. using System;
  7. using System.Xml;
  8. using System.Diagnostics;
  9. using System.Collections;
  10. namespace Microsoft.XmlDiffPatch
  11. {
  12. //////////////////////////////////////////////////////////////////
  13. // DiffgramGenerator
  14. //
  15. internal class DiffgramGenerator
  16. {
  17.     //////////////////////////////////////////////////////////////////
  18.     // DiffgramGenerator.SubstituteEditScript
  19.     //
  20.     internal struct PostponedEditScriptInfo
  21.     {
  22.         internal EditScriptPostponed _firstES;
  23.         internal EditScriptPostponed _lastES;
  24.         internal int _startSourceIndex;
  25.         internal int _endSourceIndex;
  26.         internal void Reset()
  27.         {
  28.             _firstES = null;
  29.             _lastES = null;
  30.             _startSourceIndex = 0;
  31.             _endSourceIndex = 0;
  32.         }
  33.     }
  34.     internal class PrefixChange
  35.     {
  36.         internal string _oldPrefix;
  37.         internal string _newPrefix;
  38.         internal string _NS;
  39.         internal ulong _opid;
  40.         internal PrefixChange _next;
  41.         internal PrefixChange( string oldPrefix, string newPrefix, string ns, ulong opid, 
  42.                              PrefixChange next )
  43.         {
  44.             _oldPrefix = oldPrefix;
  45.             _newPrefix = newPrefix;
  46.             _NS = ns;
  47.             _opid = opid;
  48.             _next = next;
  49.         }
  50.     }
  51.     internal class NamespaceChange
  52.     {
  53.         internal string _prefix;
  54.         internal string _oldNS;
  55.         internal string _newNS;
  56.         internal ulong _opid;
  57.         internal NamespaceChange _next;
  58.         internal NamespaceChange( string prefix, string oldNamespace, string newNamespace, 
  59.                                 ulong opid, NamespaceChange next )
  60.         {
  61.             _prefix = prefix;
  62.             _oldNS = oldNamespace;
  63.             _newNS = newNamespace;
  64.             _opid = opid;
  65.             _next = next;
  66.         }
  67.     }
  68. // Fields for both generation methods
  69.     XmlDiff _xmlDiff;
  70.     // cached !XmlDiff.IgnoreChildOrder
  71.     bool _bChildOrderSignificant;
  72.     // Descriptors & operation IDs
  73.     ulong _lastOperationID;
  74.     // 'move' descriptors
  75.     internal const int MoveHashtableInitialSize = 8;
  76.     internal Hashtable _moveDescriptors = new Hashtable( MoveHashtableInitialSize );
  77.     // 'prefix' descriptors
  78.     PrefixChange _prefixChangeDescr = null;
  79.     // 'namespace' descriptors
  80.     NamespaceChange _namespaceChangeDescr = null;
  81. // Fields for generation from edit script:
  82.     // processed edit script
  83.     EditScript _editScript;
  84.     
  85.     // nodes in the post-order numbering - cached from XmlDiff 
  86.     XmlDiffNode[] _sourceNodes;
  87.     XmlDiffNode[] _targetNodes;
  88.     // current processed nodes
  89.     int _curSourceIndex;
  90.     int _curTargetIndex;
  91.     // substitute edit script 
  92.     PostponedEditScriptInfo _postponedEditScript;
  93.     bool _bBuildingAddTree = false;
  94.     // cached DiffgramPosition object
  95.     DiffgramPosition _cachedDiffgramPosition = new DiffgramPosition( null );
  96. // Fields for generation from walktree
  97.     private const int LogMultiplier = 4;
  98. // Constructor
  99. internal DiffgramGenerator( XmlDiff xmlDiff ) 
  100.     {
  101.         Debug.Assert( xmlDiff != null );
  102.         _xmlDiff = xmlDiff;
  103.         _bChildOrderSignificant = !xmlDiff.IgnoreChildOrder;
  104.         _lastOperationID = 0;
  105.     }
  106. // Methods
  107.     internal Diffgram GenerateFromEditScript( EditScript editScript )
  108.     {
  109.         Debug.Assert( editScript != null );
  110.         Debug.Assert( _xmlDiff._sourceNodes != null );
  111.         Debug.Assert( _xmlDiff._targetNodes != null );
  112.         _sourceNodes = _xmlDiff._sourceNodes;
  113.         _targetNodes = _xmlDiff._targetNodes;
  114.         Diffgram diffgram = new Diffgram( _xmlDiff );
  115.         // root nodes always match; remove them from the edit script
  116.         EditScriptMatch esm = editScript as EditScriptMatch;
  117.         if ( editScript.Operation == EditScriptOperation.Match  && 
  118.              ( esm._firstSourceIndex + esm._length == _sourceNodes.Length  &&
  119.                esm._firstTargetIndex + esm._length == _targetNodes.Length ) )
  120.         {
  121.             esm._length--;
  122.             if ( esm._length == 0 )
  123.                 editScript = esm._nextEditScript;
  124.         }
  125.         else
  126.             Debug.Assert( false, "The root nodes does not match!" );
  127.         // init globals
  128.         _curSourceIndex = _sourceNodes.Length - 2;
  129.         _curTargetIndex = _targetNodes.Length - 2;
  130.         _editScript = editScript;
  131.         // generate diffgram
  132.         GenerateDiffgramMatch( diffgram, 1, 1 );
  133.         // add descriptors
  134.         AppendDescriptors( diffgram );
  135.         return diffgram;
  136.     }
  137.     private void AppendDescriptors( Diffgram diffgram ) {
  138.         IDictionaryEnumerator en = _moveDescriptors.GetEnumerator();
  139.         while ( en.MoveNext() )
  140.             diffgram.AddDescriptor( new OperationDescrMove( (ulong)en.Value ) );
  141.         NamespaceChange nsChange = _namespaceChangeDescr;
  142.         while ( nsChange != null )
  143.         {
  144.             diffgram.AddDescriptor( new OperationDescrNamespaceChange( nsChange ) );
  145.             nsChange = nsChange._next;
  146.         }
  147.         PrefixChange prefixChange = _prefixChangeDescr;
  148.         while ( prefixChange != null )
  149.         {
  150.             diffgram.AddDescriptor( new OperationDescrPrefixChange( prefixChange ) );
  151.             prefixChange = prefixChange._next;
  152.         }
  153.     }
  154.     private void GenerateDiffgramMatch( DiffgramParentOperation parent, int sourceBorderIndex, int targetBorderIndex )
  155.     {
  156.         bool bNeedPosition = false;
  157.         while ( _curSourceIndex >= sourceBorderIndex  ||
  158.                 _curTargetIndex >= targetBorderIndex )
  159.         {
  160.             Debug.Assert( _editScript != null );
  161.             switch ( _editScript.Operation )
  162.             {
  163.                 case EditScriptOperation.Match:
  164.                     OnMatch( parent, bNeedPosition );
  165.                     bNeedPosition = false;
  166.                     break;
  167.                 case EditScriptOperation.Add:
  168.                     bNeedPosition = OnAdd( parent, sourceBorderIndex, targetBorderIndex );
  169.                     break;
  170.                 case EditScriptOperation.Remove:
  171.                     if ( _curSourceIndex < sourceBorderIndex )
  172.                         return;
  173.                     OnRemove( parent );
  174.                     break;
  175.                 case EditScriptOperation.ChangeNode:
  176.                     if ( _curSourceIndex < sourceBorderIndex )
  177.                         return;
  178.                     OnChange( parent );
  179.                     break;
  180.                 case EditScriptOperation.EditScriptPostponed:
  181.                     if ( _curSourceIndex < sourceBorderIndex )
  182.                         return;
  183.                     OnEditScriptPostponed( parent, targetBorderIndex );
  184.                     break;
  185.                 default:
  186.                     Debug.Assert( false, "Invalid edit script operation type in final edit script." );
  187.                     break;
  188.             }
  189.         }
  190.     }
  191.     private void GenerateDiffgramAdd( DiffgramParentOperation parent, int sourceBorderIndex, int targetBorderIndex )
  192.     {
  193.         while ( _curTargetIndex >= targetBorderIndex )
  194.         {
  195.             Debug.Assert( _editScript != null );
  196.             switch ( _editScript.Operation )
  197.             {
  198.                 case EditScriptOperation.Match:
  199.                     OnMatch( parent, false );
  200.                     break;
  201.                 case EditScriptOperation.Add:
  202.                     OnAdd( parent, sourceBorderIndex, targetBorderIndex );
  203.                     break;
  204.                 case EditScriptOperation.Remove:
  205.                     OnRemove( parent );
  206.                     break;
  207.                 case EditScriptOperation.ChangeNode:
  208.                     OnChange( parent );
  209.                     break;
  210.                 case EditScriptOperation.EditScriptPostponed:
  211.                     OnEditScriptPostponed( parent, targetBorderIndex );
  212.                     break;
  213.                 default:
  214.                     Debug.Assert( false, "Invalid edit script operation type in final edit script." );
  215.                     break;
  216.             }
  217.         }
  218.     }
  219.     private void GenerateDiffgramPostponed( DiffgramParentOperation parent, ref EditScript editScript, int sourceBorderIndex, int targetBorderIndex )
  220.     {
  221.         while ( _curSourceIndex >= sourceBorderIndex  &&  editScript != null )
  222.         {
  223.             EditScriptPostponed esp = editScript as EditScriptPostponed;
  224.             if ( esp == null ) {
  225.                 GenerateDiffgramMatch( parent, sourceBorderIndex, targetBorderIndex );
  226.                 return;
  227.             }
  228.                 
  229.             Debug.Assert( esp._endSourceIndex == _curSourceIndex );
  230.             
  231.             int sourceStartIndex = esp._startSourceIndex;
  232.             int sourceLeft = _sourceNodes[ esp._endSourceIndex ].Left;
  233.             DiffgramOperation diffOp = esp._diffOperation;
  234.             // adjust current source index
  235.             _curSourceIndex = esp._startSourceIndex - 1;
  236.            
  237.             // move to next edit script
  238.             editScript = esp._nextEditScript;
  239.             // not a subtree or leaf node operation -> process child operations
  240.             if ( sourceStartIndex > sourceLeft ) {
  241.                 GenerateDiffgramPostponed( (DiffgramParentOperation) diffOp, ref editScript, sourceLeft, targetBorderIndex );
  242.             }
  243.             // insert operation
  244.             parent.InsertAtBeginning( diffOp );
  245.         }
  246.     }
  247.     private void OnMatch( DiffgramParentOperation parent, bool bNeedPosition )
  248.     {
  249.         EditScriptMatch matchOp = _editScript as EditScriptMatch;
  250.         Debug.Assert( _curSourceIndex == matchOp._firstSourceIndex + matchOp._length - 1 );
  251.         Debug.Assert( _curTargetIndex == matchOp._firstTargetIndex + matchOp._length - 1 );
  252.         // cache
  253.         int endTargetIndex = matchOp._firstTargetIndex + matchOp._length - 1;
  254.         int endSourceIndex = matchOp._firstSourceIndex + matchOp._length - 1;
  255.         XmlDiffNode targetRoot = _targetNodes[ endTargetIndex ];
  256.         XmlDiffNode sourceRoot = _sourceNodes[ endSourceIndex ];
  257.         // a subtree or leaf node matches
  258.         if ( matchOp._firstTargetIndex <= targetRoot.Left  &&
  259.              matchOp._firstSourceIndex <= sourceRoot.Left )
  260.         {
  261.             if ( _bBuildingAddTree )
  262.             {
  263.                 Debug.Assert( !bNeedPosition );
  264.                 ulong opid = GenerateOperationID( XmlDiffDescriptorType.Move );
  265.                 // output <add match=" "> to diffgram and "remove" to substitute script
  266.                 parent.InsertAtBeginning( new DiffgramCopy( sourceRoot, true, opid ) );
  267.                 // add 'remove' operation to postponed operations
  268.                 PostponedRemoveSubtrees( sourceRoot, opid, 
  269.                 //AddToPosponedOperations( new DiffgramRemoveSubtrees( sourceRoot, opid ), 
  270.                                         sourceRoot.Left, 
  271.                                         endSourceIndex );
  272.             }
  273.             else
  274.             {
  275.                 // matched element -> check attributes if they really match (hash values of attributes matches)
  276.                 if ( sourceRoot.NodeType == XmlDiffNodeType.Element )
  277.                 {
  278.                     DiffgramPosition diffPos = _cachedDiffgramPosition;
  279.                     diffPos._sourceNode = sourceRoot;
  280.                     GenerateChangeDiffgramForAttributes( diffPos, (XmlDiffElement)sourceRoot, (XmlDiffElement)targetRoot );
  281.                     if ( diffPos._firstChildOp != null || bNeedPosition )
  282.                     {
  283.                         parent.InsertAtBeginning( diffPos );
  284.                         _cachedDiffgramPosition = new DiffgramPosition( null );
  285.                         bNeedPosition = false;
  286.                     }
  287.                 }
  288.                 // otherwise output <node> - only if we need the position (<=> preceding operation was 'add')
  289.                 else
  290.                 {
  291.                     if ( bNeedPosition )
  292.                     {
  293.                         parent.InsertAtBeginning( new DiffgramPosition( sourceRoot, targetRoot ) );
  294.                         bNeedPosition = false;
  295.                     }
  296.                     // xml declaration, DTD
  297.                     else if ( !_bChildOrderSignificant && (int)sourceRoot.NodeType < 0 ) {
  298.                         DiffgramOperation op = parent._firstChildOp;
  299.                         if ( op is DiffgramAddNode || op is DiffgramAddSubtrees || op is DiffgramCopy ) {
  300.                             parent.InsertAtBeginning( new DiffgramPosition( sourceRoot, targetRoot ) );
  301.                         }
  302.                     }
  303.                 }
  304.             }
  305.             // adjust current position
  306.             _curSourceIndex = sourceRoot.Left - 1;
  307.             _curTargetIndex = targetRoot.Left - 1;
  308.             // adjust boundaries in the edit script or move to next edit script operation
  309.             matchOp._length -= endTargetIndex - targetRoot.Left + 1;
  310.             if ( matchOp._length <= 0 )
  311.                 _editScript = _editScript._nextEditScript;
  312.         }
  313.         // single but non-leaf node matches (-> recursively generate the diffgram subtree)
  314.         else
  315.         {
  316.             // adjust current position
  317.             _curSourceIndex--;
  318.             _curTargetIndex--;
  319.             // adjust boundaries in the edit script or move to next edit script operation
  320.             matchOp._length--;
  321.             if ( matchOp._length <= 0 )
  322.                 _editScript = _editScript._nextEditScript;
  323.             DiffgramParentOperation diffgramNode;
  324.             if ( _bBuildingAddTree )
  325.             {
  326.                 Debug.Assert( !bNeedPosition );
  327.                 ulong opid = GenerateOperationID( XmlDiffDescriptorType.Move );
  328.                 bool bCopySubtree = sourceRoot.NodeType != XmlDiffNodeType.Element;
  329.                 // output <add match=".." subtree="no">
  330.                 diffgramNode = new DiffgramCopy( sourceRoot, bCopySubtree, opid );
  331.                 // add 'remove' operation to postponed operations
  332.                 PostponedRemoveNode( sourceRoot, bCopySubtree, opid, 
  333.                                      endSourceIndex, 
  334.                                      endSourceIndex );
  335.                 // recursively generate the diffgram subtree
  336.                 GenerateDiffgramAdd( diffgramNode, sourceRoot.Left, targetRoot.Left );
  337.                 // insert to diffgram tree
  338.                 parent.InsertAtBeginning( diffgramNode );
  339.             }
  340.             else
  341.             {
  342.                 // output <node>
  343.                 diffgramNode = new DiffgramPosition( sourceRoot, targetRoot );
  344.                 // recursively generate the diffgram subtree
  345.                 GenerateDiffgramMatch( diffgramNode, sourceRoot.Left, targetRoot.Left );
  346.                 // insert to diffgram tree
  347.                 if ( diffgramNode._firstChildOp != null )
  348.                     parent.InsertAtBeginning( diffgramNode );
  349.             }
  350.         }
  351.     }
  352.     // returns true if a positioning <node> element is needed in diffgram
  353.     private bool OnAdd( DiffgramParentOperation parent, int sourceBorderIndex, int targetBorderIndex )
  354.     {
  355.         EditScriptAdd addOp = _editScript as EditScriptAdd;
  356.         
  357.         Debug.Assert( addOp._endTargetIndex == _curTargetIndex );
  358.         XmlDiffNode targetRoot = _targetNodes[ addOp._endTargetIndex ];
  359.         // add subtree or leaf node and no descendant node matches (= has been moved from somewhere else)
  360.         if ( addOp._startTargetIndex <= targetRoot.Left &&
  361.              !targetRoot._bSomeDescendantMatches )
  362.         {
  363.             switch ( targetRoot.NodeType )
  364.             {
  365.                 case XmlDiffNodeType.ShrankNode:
  366.     XmlDiffShrankNode shrankNode = (XmlDiffShrankNode)targetRoot;
  367.                     if ( _xmlDiff.IgnoreChildOrder && ParentMatches( shrankNode.MatchingShrankNode, shrankNode, parent ) ) {
  368.                         break;
  369.                     }
  370.                     else {
  371.         if ( shrankNode.MoveOperationId == 0 )
  372.         shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  373.         parent.InsertAtBeginning( new DiffgramCopy( shrankNode.MatchingShrankNode, true, shrankNode.MoveOperationId ) );
  374.                     }
  375.                     break;
  376.                 case XmlDiffNodeType.XmlDeclaration:
  377.                 case XmlDiffNodeType.DocumentType:
  378.                 case XmlDiffNodeType.EntityReference:
  379.      parent.InsertAtBeginning( new DiffgramAddNode( targetRoot, 0 ) );
  380.                     break;
  381.                 default:
  382.                     if ( !parent.MergeAddSubtreeAtBeginning( targetRoot ) )
  383.                     {
  384.                         parent.InsertAtBeginning( new DiffgramAddSubtrees( targetRoot, 0, !_xmlDiff.IgnoreChildOrder ) );
  385.                     }
  386.                     break;
  387.             }
  388.             // adjust current position
  389.             _curTargetIndex = targetRoot.Left - 1;
  390.             // adjust boundaries in the edit script or move to next edit script operation
  391.             addOp._endTargetIndex = targetRoot.Left - 1;
  392.             if ( addOp._startTargetIndex > addOp._endTargetIndex )
  393.                 _editScript = _editScript._nextEditScript;
  394.         }
  395.         // add single but non-leaf node, or some descendant matches (= has been moved from somewhere else )
  396.         // -> recursively process diffgram subtree  
  397.         else
  398.         {
  399. Debug.Assert( !( targetRoot is XmlDiffShrankNode) );
  400.             DiffgramAddNode addNode = new DiffgramAddNode( targetRoot, 0 );
  401.             // adjust current position
  402.             _curTargetIndex--;
  403.             // adjust boundaries in the edit script or move to next edit script operation
  404.             addOp._endTargetIndex--;
  405.             if ( addOp._startTargetIndex > addOp._endTargetIndex )
  406.                 _editScript = _editScript._nextEditScript;
  407.             if ( _bBuildingAddTree )
  408.             {
  409.                 GenerateDiffgramAdd( addNode, sourceBorderIndex, targetRoot.Left );
  410.             }
  411.             else
  412.             {
  413.                 // switch to 'building add-tree' mode
  414.                 _postponedEditScript.Reset();
  415.                 _bBuildingAddTree = true;
  416.                 // generate new tree
  417.                 GenerateDiffgramAdd( addNode, sourceBorderIndex, targetRoot.Left );
  418.                 _bBuildingAddTree = false;
  419.                 // attach postponed edit script to _editScript for futher processing
  420.                 if ( _postponedEditScript._firstES != null )
  421.                 {
  422.                     Debug.Assert( _postponedEditScript._lastES != null );
  423.                     Debug.Assert( _postponedEditScript._startSourceIndex != 0 );
  424.                     Debug.Assert( _postponedEditScript._endSourceIndex != 0 );
  425.                     _curSourceIndex = _postponedEditScript._endSourceIndex;
  426.                     _postponedEditScript._lastES._nextEditScript = _editScript;
  427.                     _editScript = _postponedEditScript._firstES;
  428.                 }
  429.             }
  430.             // add attributes
  431.             if ( targetRoot.NodeType == XmlDiffNodeType.Element ) 
  432.                 GenerateAddDiffgramForAttributes( addNode, (XmlDiffElement)targetRoot );
  433.             
  434.             parent.InsertAtBeginning( addNode );
  435.         }
  436.         // return true if positioning <node> element is needed in diffgram
  437.         if ( _bChildOrderSignificant ) {
  438.             return !_bBuildingAddTree;
  439.         }
  440.         else {
  441.             return false;
  442.         }
  443.     }
  444.     private void OnRemove( DiffgramParentOperation parent )
  445.     {
  446.         EditScriptRemove remOp = _editScript as EditScriptRemove;
  447.         Debug.Assert( remOp._endSourceIndex == _curSourceIndex );
  448.         XmlDiffNode sourceRoot = _sourceNodes[ remOp._endSourceIndex ];
  449.         // remove subtree or leaf node and no descendant node matches (=has been moved somewhere else)
  450.         if ( remOp._startSourceIndex <= sourceRoot.Left )
  451.         {
  452.             bool bShrankNode = sourceRoot is XmlDiffShrankNode;
  453.             if ( sourceRoot._bSomeDescendantMatches && !bShrankNode )
  454.             {
  455.                 DiffgramOperation newDiffOp = GenerateDiffgramRemoveWhenDescendantMatches( (XmlDiffParentNode)sourceRoot );
  456.                 if ( _bBuildingAddTree )
  457.                 {
  458.                     PostponedOperation( newDiffOp, sourceRoot.Left, remOp._endSourceIndex );
  459.                 }
  460.                 else
  461.                 {
  462.                     parent.InsertAtBeginning( newDiffOp );
  463.                 }
  464.             }
  465.             else
  466.             {
  467.     ulong opid = 0;
  468.                 // shrank node -> output as 'move' operation
  469.     if ( bShrankNode )
  470.     {
  471.     XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) sourceRoot;
  472.                     if ( _xmlDiff.IgnoreChildOrder && ParentMatches( shrankNode, shrankNode.MatchingShrankNode, parent ) ) {
  473.                         goto End;
  474.                     }
  475.     if ( shrankNode.MoveOperationId == 0 )
  476.     shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  477.     opid = shrankNode.MoveOperationId;
  478.     Debug.Assert( sourceRoot == _sourceNodes[ sourceRoot.Left ] );
  479.     }
  480.                 // insert 'remove' operation 
  481.     if ( _bBuildingAddTree )
  482.                 {
  483.                     PostponedRemoveSubtrees( sourceRoot, opid, sourceRoot.Left, remOp._endSourceIndex );
  484.                 }
  485.     else
  486.     {
  487.     if ( opid != 0 ||
  488.     !parent.MergeRemoveSubtreeAtBeginning( sourceRoot ) )
  489.     {
  490.     parent.InsertAtBeginning( new DiffgramRemoveSubtrees( sourceRoot, opid, !_xmlDiff.IgnoreChildOrder ) );
  491.     }
  492.     }
  493.             }
  494.         End:
  495.             // adjust current position
  496.             _curSourceIndex = sourceRoot.Left - 1;
  497.             // adjust boundaries in the edit script or move to next edit script operation
  498.             remOp._endSourceIndex = sourceRoot.Left - 1;
  499.             if ( remOp._startSourceIndex > remOp._endSourceIndex )
  500.                 _editScript = _editScript._nextEditScript;
  501.         }
  502.         // remove single but non-leaf node or some descendant matches (=has been moved somewhere else)
  503.         // -> recursively process diffgram subtree  
  504.         else
  505.         {
  506. Debug.Assert( !( sourceRoot is XmlDiffShrankNode) );
  507.             // adjust current position
  508.             _curSourceIndex--;
  509.             // adjust boundaries in the edit script or move to next edit script operation
  510.             remOp._endSourceIndex--;
  511.             if ( remOp._startSourceIndex > remOp._endSourceIndex )
  512.                 _editScript = _editScript._nextEditScript;
  513.             bool bRemoveSubtree = sourceRoot.NodeType != XmlDiffNodeType.Element;
  514.             if ( _bBuildingAddTree )
  515.             {
  516.                 // add 'remove' to postponed operations 
  517.                 PostponedRemoveNode( sourceRoot, bRemoveSubtree, 0,
  518.                 //AddToPosponedOperations( new DiffgramRemoveNode( sourceRoot, bRemoveSubtree, 0 ), 
  519.                     remOp._endSourceIndex + 1, remOp._endSourceIndex + 1 );
  520.                 
  521.                 // recursively parse subtree
  522.                 GenerateDiffgramAdd( parent, sourceRoot.Left, _targetNodes[ _curTargetIndex ].Left );
  523.             }
  524.             else
  525.             {
  526.                 // 'remove' operation
  527.                 DiffgramRemoveNode remNode = new DiffgramRemoveNode( sourceRoot, bRemoveSubtree, 0 );
  528.                 
  529.                 // parse subtree
  530.                 GenerateDiffgramMatch( remNode, sourceRoot.Left, _targetNodes[ _curTargetIndex ].Left );
  531.                 
  532.                 parent.InsertAtBeginning( remNode );
  533.             }
  534.         }
  535.     }
  536.     // produces <change> element in diffgram
  537.     private void OnChange( DiffgramParentOperation parent )
  538.     {
  539.         EditScriptChange chOp = _editScript as EditScriptChange;
  540.         Debug.Assert( chOp._targetIndex == _curTargetIndex );
  541.         Debug.Assert( chOp._sourceIndex == _curSourceIndex );
  542.         XmlDiffNode sourceRoot = _sourceNodes[ chOp._sourceIndex ];
  543.         XmlDiffNode targetRoot = _targetNodes[ chOp._targetIndex ];
  544. Debug.Assert( !( sourceRoot is XmlDiffShrankNode) );
  545. Debug.Assert( !( targetRoot is XmlDiffShrankNode) );
  546.         // adjust current position
  547.         _curSourceIndex--;
  548.         _curTargetIndex--;
  549.         // move to next edit script operation
  550.         _editScript = _editScript._nextEditScript;
  551.         DiffgramOperation diffgramNode = null;
  552.         if ( _bBuildingAddTree )
  553.         {
  554.             // <add> changed node to the new location
  555.             if ( targetRoot.NodeType == XmlDiffNodeType.Element )
  556.                 diffgramNode = new DiffgramAddNode( targetRoot, 0 );
  557.             else
  558.                 diffgramNode = new DiffgramAddSubtrees( targetRoot, 0, !_xmlDiff.IgnoreChildOrder );
  559.             // <remove> old node from old location -> add to postponed operations
  560.             bool bSubtree = sourceRoot.NodeType != XmlDiffNodeType.Element;
  561.             PostponedRemoveNode( sourceRoot, bSubtree, 0,
  562.             //AddToPosponedOperations( new DiffgramRemoveNode( sourceRoot, bSubtree, 0 ), 
  563.                 chOp._sourceIndex, chOp._sourceIndex );
  564.             // recursively process children
  565.             if ( sourceRoot.Left < chOp._sourceIndex ||
  566.                 targetRoot.Left < chOp._targetIndex )
  567.             {
  568.                 Debug.Assert( targetRoot.NodeType == XmlDiffNodeType.Element );
  569.                 GenerateDiffgramAdd( (DiffgramParentOperation)diffgramNode, sourceRoot.Left, targetRoot.Left );
  570.             }
  571.             // add attributes, if element
  572.             if ( targetRoot.NodeType == XmlDiffNodeType.Element )
  573.                 GenerateAddDiffgramForAttributes( (DiffgramParentOperation)diffgramNode, (XmlDiffElement)targetRoot );
  574.         }
  575.         else
  576.         {
  577.             ulong opid = 0;
  578.             // change of namespace or prefix -> get the appropriate operation id
  579.             if ( !_xmlDiff.IgnoreNamespaces && 
  580.                  sourceRoot.NodeType == XmlDiffNodeType.Element )
  581.             {
  582.                 XmlDiffElement sourceEl = (XmlDiffElement)sourceRoot;
  583.                 XmlDiffElement targetEl = (XmlDiffElement)targetRoot;
  584.                 if ( sourceEl.LocalName == targetEl.LocalName )
  585.                 {
  586.                     opid = GetNamespaceChangeOpid( sourceEl.NamespaceURI, sourceEl.Prefix,
  587.                                                    targetEl.NamespaceURI, targetEl.Prefix );
  588.                 }  
  589.             }
  590.             if ( sourceRoot.NodeType == XmlDiffNodeType.Element )
  591.             {
  592.                 if ( XmlDiff.IsChangeOperationOnAttributesOnly( chOp._changeOp ) )
  593.                     diffgramNode = new DiffgramPosition( sourceRoot );
  594.                 else
  595.                 {
  596.                     Debug.Assert( (int)chOp._changeOp == (int)XmlDiffOperation.ChangeElementName ||
  597.                                   ( (int)chOp._changeOp >= (int)XmlDiffOperation.ChangeElementNameAndAttr1 &&
  598.                                     (int)chOp._changeOp <= (int)XmlDiffOperation.ChangeElementNameAndAttr2 ) );
  599.                     diffgramNode = new DiffgramChangeNode( sourceRoot, targetRoot, XmlDiffOperation.ChangeElementName, opid );
  600.                 }
  601.                 // recursively process children
  602.                 if ( sourceRoot.Left < chOp._sourceIndex ||
  603.                     targetRoot.Left < chOp._targetIndex )
  604.                 {
  605.                     GenerateDiffgramMatch( (DiffgramParentOperation) diffgramNode, sourceRoot.Left, targetRoot.Left );
  606.                 }
  607.                 GenerateChangeDiffgramForAttributes( (DiffgramParentOperation)diffgramNode, (XmlDiffElement)sourceRoot, (XmlDiffElement)targetRoot );
  608.             }
  609.             else
  610.             {
  611.                 // '<change>'
  612.                 diffgramNode = new DiffgramChangeNode( sourceRoot, targetRoot, chOp._changeOp, opid );
  613.                 Debug.Assert( !sourceRoot.HasChildNodes );
  614.             }
  615.         }
  616.         parent.InsertAtBeginning( diffgramNode );
  617.     }
  618.     private void PostponedRemoveNode( XmlDiffNode sourceNode, bool bSubtree, ulong operationID,
  619.                                       int startSourceIndex, int endSourceIndex )
  620.     {
  621.         Debug.Assert( sourceNode != null );
  622.         PostponedOperation( new DiffgramRemoveNode( sourceNode, bSubtree, operationID ), startSourceIndex, endSourceIndex );
  623.     }
  624.     private void PostponedRemoveSubtrees( XmlDiffNode sourceNode, ulong operationID,
  625.                                           int startSourceIndex, int endSourceIndex )
  626.     {
  627.         Debug.Assert( _bBuildingAddTree );
  628.         Debug.Assert( sourceNode != null );
  629.         if ( operationID == 0 &&
  630.              _postponedEditScript._firstES != null )
  631.         {
  632.             Debug.Assert( _postponedEditScript._lastES._startSourceIndex > endSourceIndex );
  633.             DiffgramRemoveSubtrees remSubtrees = _postponedEditScript._lastES._diffOperation as DiffgramRemoveSubtrees;
  634.             if ( remSubtrees != null  && 
  635.                 remSubtrees.SetNewFirstNode( sourceNode ) )
  636.             {
  637.                 _postponedEditScript._lastES._startSourceIndex = startSourceIndex;
  638.                 _postponedEditScript._startSourceIndex = startSourceIndex;
  639.                 return;
  640.             }
  641.         }
  642.         
  643.         PostponedOperation( new DiffgramRemoveSubtrees( sourceNode, operationID, !_xmlDiff.IgnoreChildOrder ), startSourceIndex, endSourceIndex );
  644.     }
  645.     private void PostponedOperation( DiffgramOperation op, int startSourceIndex, int endSourceIndex )
  646.     {
  647.         Debug.Assert( _bBuildingAddTree );
  648.         Debug.Assert( op != null );
  649.         EditScriptPostponed es = new EditScriptPostponed( op, startSourceIndex, endSourceIndex );
  650.         if ( _postponedEditScript._firstES == null )
  651.         {
  652.             _postponedEditScript._firstES = es;
  653.             _postponedEditScript._lastES = es;
  654.             _postponedEditScript._startSourceIndex = startSourceIndex;
  655.             _postponedEditScript._endSourceIndex = endSourceIndex;
  656.         }
  657.         else
  658.         {
  659.             Debug.Assert( _postponedEditScript._lastES != null );
  660.             Debug.Assert( _postponedEditScript._lastES._startSourceIndex > endSourceIndex );
  661.             _postponedEditScript._lastES._nextEditScript = es;
  662.             _postponedEditScript._lastES = es;
  663.             _postponedEditScript._startSourceIndex = startSourceIndex;
  664.         }
  665.     }
  666.     // generates a new operation ID
  667.     private ulong GenerateOperationID( XmlDiffDescriptorType descriptorType )
  668.     {
  669.         ulong opid = ++_lastOperationID;
  670.         if ( descriptorType == XmlDiffDescriptorType.Move )
  671.             _moveDescriptors.Add( opid, opid );
  672.         return opid;
  673.     }
  674.     // returns appropriate operation ID for this namespace or prefix change
  675.     private ulong GetNamespaceChangeOpid( string oldNamespaceURI, string oldPrefix, 
  676.                                           string newNamespaceURI, string newPrefix )
  677.     {
  678.         Debug.Assert( !_xmlDiff.IgnoreNamespaces );
  679.         ulong opid = 0;
  680.         // namespace change
  681.         if ( oldNamespaceURI != newNamespaceURI )
  682.         {
  683.             // prefix must remain the same
  684.             if ( oldPrefix != newPrefix )
  685.                 return 0;
  686.             // lookup this change in the list of namespace changes
  687.             NamespaceChange nsChange = _namespaceChangeDescr;
  688.             while ( nsChange != null )
  689.             {
  690.                 if ( nsChange._oldNS == oldNamespaceURI &&
  691.                      nsChange._prefix == oldPrefix &&
  692.                      nsChange._newNS == newNamespaceURI )
  693.                 {
  694.                     return nsChange._opid;
  695.                 }
  696.                 nsChange = nsChange._next;
  697.             }
  698.             
  699.             // the change record was not found -> create a new one
  700.             opid = GenerateOperationID( XmlDiffDescriptorType.NamespaceChange );
  701.             _namespaceChangeDescr = new NamespaceChange( oldPrefix, oldNamespaceURI, newNamespaceURI,
  702.                                                          opid, _namespaceChangeDescr );
  703.         }
  704.         // prefix change
  705.         else if ( !_xmlDiff.IgnorePrefixes && 
  706.                   oldPrefix != newPrefix )
  707.         {
  708.             // lookup this change in the list of prefix changes
  709.             PrefixChange prefixChange = _prefixChangeDescr;
  710.             while ( prefixChange != null )
  711.             {
  712.                 if ( prefixChange._NS == oldNamespaceURI &&
  713.                      prefixChange._oldPrefix == oldPrefix &&
  714.                      prefixChange._newPrefix == newPrefix )
  715.                 {
  716.                     return prefixChange._opid;
  717.                 }
  718.                 prefixChange = prefixChange._next;
  719.             }
  720.             // the change record was not found -> create a new one
  721.             opid = GenerateOperationID( XmlDiffDescriptorType.PrefixChange );
  722.             _prefixChangeDescr = new PrefixChange( oldPrefix, newPrefix, 
  723.                                                    oldNamespaceURI,
  724.                                                    opid, 
  725.                                                    _prefixChangeDescr );
  726.         }
  727.         return opid;
  728.     }
  729. internal Diffgram GenerateEmptyDiffgram()
  730. {
  731. return new Diffgram( _xmlDiff );
  732. }
  733.     private void GenerateChangeDiffgramForAttributes( DiffgramParentOperation diffgramParent, 
  734.                                                       XmlDiffElement sourceElement, 
  735.                                                       XmlDiffElement targetElement )
  736.     {
  737.         XmlDiffAttributeOrNamespace sourceAttr = sourceElement._attributes;
  738.         XmlDiffAttributeOrNamespace targetAttr = targetElement._attributes;
  739.         int nCompare;
  740.         ulong opid;
  741.         while ( sourceAttr != null && targetAttr != null ) 
  742.         {
  743.             opid = 0;
  744.             if ( sourceAttr.NodeType == targetAttr.NodeType )
  745.             {
  746.                 if ( sourceAttr.NodeType == XmlDiffNodeType.Attribute ) 
  747.                 {
  748.                     if ( (nCompare = XmlDiffDocument.OrderStrings( sourceAttr.LocalName, targetAttr.LocalName )) == 0 )
  749.                     {
  750.                         if ( _xmlDiff.IgnoreNamespaces )
  751.                         {
  752.                             if ( XmlDiffDocument.OrderStrings( sourceAttr.Value, targetAttr.Value ) == 0 )
  753.                             {
  754.                                 // attributes match
  755.                                 goto Next;
  756.                             }
  757.                         }
  758.                         else
  759.                         {
  760.                             if ( XmlDiffDocument.OrderStrings( sourceAttr.NamespaceURI, targetAttr.NamespaceURI ) == 0  &&  
  761.                          (_xmlDiff.IgnorePrefixes || XmlDiffDocument.OrderStrings( sourceAttr.Prefix, targetAttr.Prefix )  == 0 ) &&
  762.                                 XmlDiffDocument.OrderStrings( sourceAttr.Value, targetAttr.Value ) == 0 )
  763.                             {
  764.                                 // attributes match
  765.                                 goto Next;
  766.                             }
  767.                         }
  768.                         diffgramParent.InsertAtBeginning( new DiffgramChangeNode( sourceAttr, targetAttr, XmlDiffOperation.ChangeAttr, 0 ) );
  769.                         goto Next;
  770.                     }
  771.                     goto AddRemove;
  772.                 }
  773.                 else // sourceAttr.NodeType != XmlDiffNodeType.Attribute 
  774.                 {
  775.                     if ( _xmlDiff.IgnorePrefixes ) {
  776.                         if ( ( nCompare = XmlDiffDocument.OrderStrings( sourceAttr.NamespaceURI, targetAttr.NamespaceURI ) ) == 0 )
  777.                             goto Next;
  778.                         else
  779.                             goto AddRemove;
  780.                     }
  781.                     else if ( ( nCompare = XmlDiffDocument.OrderStrings( sourceAttr.Prefix, targetAttr.Prefix ) ) == 0 ) {
  782.                         if ( ( nCompare = XmlDiffDocument.OrderStrings( sourceAttr.NamespaceURI, targetAttr.NamespaceURI ) ) == 0 )
  783.                             goto Next;
  784.                         else {
  785.                             // change of namespace
  786.                             opid = GetNamespaceChangeOpid( sourceAttr.NamespaceURI, sourceAttr.Prefix, 
  787.                                                         targetAttr.NamespaceURI, targetAttr.Prefix );
  788.                             goto AddRemoveBoth;
  789.                         }
  790.                     }
  791.                     else {
  792.                         if ( ( nCompare = XmlDiffDocument.OrderStrings( sourceAttr.NamespaceURI, targetAttr.NamespaceURI ) ) == 0 ) {
  793.                             // change of prefix
  794.                             opid = GetNamespaceChangeOpid( sourceAttr.NamespaceURI, sourceAttr.Prefix, 
  795.                                                         targetAttr.NamespaceURI, targetAttr.Prefix );
  796.                             goto AddRemoveBoth;
  797.                         }
  798.                         else {
  799.                             goto AddRemove;
  800.                         }
  801.                     }
  802.                 }
  803.             }
  804.             else // ( sourceAttr.NodeType != targetAttr.NodeType )
  805.             {
  806.     if ( sourceAttr.NodeType == XmlDiffNodeType.Namespace )
  807.          goto RemoveSource;
  808.          else
  809.          goto AddTarget;
  810.             }
  811.         Next:
  812.             sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  813.             targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  814.             continue;
  815.         AddRemove:
  816.             if ( nCompare == -1 )
  817.                 goto RemoveSource;
  818.             else
  819.             {
  820.                 Debug.Assert( nCompare == 1 );
  821.                 goto AddTarget;
  822.             }
  823.                 
  824.         AddRemoveBoth:
  825.             if ( !diffgramParent.MergeRemoveAttributeAtBeginning( sourceAttr ) )
  826.                 diffgramParent.InsertAtBeginning( new DiffgramRemoveNode( sourceAttr, true, opid ) );
  827.             sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  828.             diffgramParent.InsertAtBeginning( new DiffgramAddNode( targetAttr, opid ) );
  829.             targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  830.             continue;
  831.             
  832.         RemoveSource:
  833.             if ( !diffgramParent.MergeRemoveAttributeAtBeginning( sourceAttr ) )
  834.                 diffgramParent.InsertAtBeginning( new DiffgramRemoveNode( sourceAttr, true, opid ) );
  835.             sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  836.             continue;
  837.                 
  838.         AddTarget:
  839.             diffgramParent.InsertAtBeginning( new DiffgramAddNode( targetAttr, opid ) );
  840.             targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  841.             continue;
  842.         }
  843.         while ( sourceAttr != null ) 
  844.         {
  845.             if ( !diffgramParent.MergeRemoveAttributeAtBeginning( sourceAttr ) )
  846.                 diffgramParent.InsertAtBeginning( new DiffgramRemoveNode( sourceAttr, true, 0 ) );
  847.             sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  848.         }
  849.         while ( targetAttr != null )
  850.         {
  851.             diffgramParent.InsertAtBeginning( new DiffgramAddNode( targetAttr, 0 ) );
  852.             targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  853.         }
  854.     }
  855.     private void GenerateAddDiffgramForAttributes( DiffgramParentOperation diffgramParent, XmlDiffElement targetElement )
  856.     {
  857.         XmlDiffAttributeOrNamespace attr = targetElement._attributes;
  858.         while ( attr != null )
  859.         {
  860.             diffgramParent.InsertAtBeginning( new DiffgramAddNode( attr, 0 ) );
  861.             attr = (XmlDiffAttributeOrNamespace)attr._nextSibling;
  862.         }
  863.     }
  864.     private DiffgramOperation GenerateDiffgramRemoveWhenDescendantMatches( XmlDiffNode sourceParent )
  865.     {
  866.         Debug.Assert( sourceParent._bSomeDescendantMatches );
  867.         Debug.Assert( sourceParent.NodeType != XmlDiffNodeType.ShrankNode );
  868.         DiffgramParentOperation diffOp = new DiffgramRemoveNode( sourceParent, false, 0 );
  869.         XmlDiffNode child = ((XmlDiffParentNode)sourceParent)._firstChildNode;
  870.         while ( child != null )
  871.         {
  872.             if ( child.NodeType == XmlDiffNodeType.ShrankNode )
  873.             {
  874.                 XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) child;
  875.                 if ( shrankNode.MoveOperationId == 0 )
  876.      shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  877. diffOp.InsertAtEnd( new DiffgramRemoveSubtrees( child, shrankNode.MoveOperationId, !_xmlDiff.IgnoreChildOrder ) );
  878.             }
  879.             else if ( child.HasChildNodes && child._bSomeDescendantMatches )
  880.             {
  881.                 diffOp.InsertAtEnd( GenerateDiffgramRemoveWhenDescendantMatches( (XmlDiffParentNode)child ) );
  882.             }
  883.             else
  884.             {
  885.                 if ( !diffOp.MergeRemoveSubtreeAtEnd( child ) )
  886.                     diffOp.InsertAtEnd( new DiffgramRemoveSubtrees( child, 0, !_xmlDiff.IgnoreChildOrder ) );
  887.             }
  888.             child = child._nextSibling;
  889.         }
  890.         return diffOp;
  891.     }
  892.     private DiffgramOperation GenerateDiffgramAddWhenDescendantMatches( XmlDiffNode targetParent ) {
  893.         Debug.Assert( targetParent.HasChildNodes );
  894.         Debug.Assert( targetParent._bSomeDescendantMatches );
  895.         Debug.Assert( targetParent.NodeType != XmlDiffNodeType.ShrankNode );
  896.         DiffgramParentOperation diffOp = new DiffgramAddNode( targetParent, 0 );
  897.         if ( targetParent.NodeType == XmlDiffNodeType.Element ) {
  898.             XmlDiffAttributeOrNamespace attr = ((XmlDiffElement)targetParent)._attributes;
  899.             while ( attr != null ) {
  900.                 diffOp.InsertAtEnd( new DiffgramAddNode( attr, 0 ) );
  901.                 attr = (XmlDiffAttributeOrNamespace) attr._nextSibling;
  902.             }
  903.         }
  904.         
  905.         XmlDiffNode child = ((XmlDiffParentNode)targetParent)._firstChildNode;
  906.         while ( child != null )
  907.         {
  908.             if ( child.NodeType == XmlDiffNodeType.ShrankNode )
  909.             {
  910.                 XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) child;
  911.                 if ( shrankNode.MoveOperationId == 0 )
  912.      shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  913. diffOp.InsertAtEnd( new DiffgramCopy( shrankNode.MatchingShrankNode, true, shrankNode.MoveOperationId ) );
  914.             }
  915.             else if ( child.HasChildNodes && child._bSomeDescendantMatches )
  916.             {
  917.                 diffOp.InsertAtEnd( GenerateDiffgramAddWhenDescendantMatches( (XmlDiffParentNode)child ) );
  918.             }
  919.             else
  920.             {
  921.                 if ( !diffOp.MergeAddSubtreeAtEnd( child ) )
  922.                     diffOp.InsertAtEnd( new DiffgramAddSubtrees( child, 0, !_xmlDiff.IgnoreChildOrder ) );
  923.             }
  924.             child = child._nextSibling;
  925.         }
  926.         return diffOp;
  927.     }
  928.     void OnEditScriptPostponed( DiffgramParentOperation parent, int targetBorderIndex ) {
  929.         EditScriptPostponed esp = (EditScriptPostponed)_editScript;
  930.         Debug.Assert( _curSourceIndex == esp._endSourceIndex );
  931.         DiffgramOperation diffOp = esp._diffOperation;
  932.         int sourceStartIndex = esp._startSourceIndex;
  933.         int sourceLeft = _sourceNodes[ esp._endSourceIndex ].Left;
  934.         // adjust current source index
  935.         _curSourceIndex = esp._startSourceIndex - 1;
  936.            
  937.         // move to next edit script
  938.         _editScript = esp._nextEditScript;
  939.         // not a subtree or leaf node operation -> process child operations
  940.         if ( sourceStartIndex > sourceLeft ) {
  941.             GenerateDiffgramPostponed( (DiffgramParentOperation)diffOp, ref _editScript, sourceLeft, targetBorderIndex );
  942.         }
  943.         parent.InsertAtBeginning( diffOp );
  944.     }
  945. // Walktree diffgram generation
  946.     internal Diffgram GenerateFromWalkTree() {
  947.         Diffgram diffgram = new Diffgram( _xmlDiff );
  948.         WalkTreeGenerateDiffgramMatch( diffgram, _xmlDiff._sourceDoc, _xmlDiff._targetDoc );
  949.         AppendDescriptors( diffgram );
  950.         return diffgram;
  951.     }
  952.     private void WalkTreeGenerateDiffgramMatch( DiffgramParentOperation diffParent, XmlDiffParentNode sourceParent, XmlDiffParentNode targetParent ) {
  953.         XmlDiffNode sourceNode = sourceParent.FirstChildNode;
  954.         XmlDiffNode targetNode = targetParent.FirstChildNode;
  955.         XmlDiffNode needPositionSourceNode = null;
  956.         while ( sourceNode != null || targetNode != null ) {
  957.             if ( sourceNode != null ) {
  958.                 if ( targetNode != null ) {
  959.                     XmlDiffOperation op = sourceNode.GetDiffOperation( targetNode, _xmlDiff );
  960.                     // match
  961.                     if ( op == XmlDiffOperation.Match ) {
  962.                         WalkTreeOnMatchNode( diffParent, sourceNode, targetNode, ref needPositionSourceNode );
  963.                     }
  964.                     // no match
  965.                     else {
  966.                         int walkNodesCount = ( sourceNode._parent.ChildNodesCount + targetNode._parent.ChildNodesCount ) / 2;
  967.                         walkNodesCount = (int)( LogMultiplier * Math.Log( (double)walkNodesCount ) + 1 );
  968.                         XmlDiffNode sourceClosestMatchNode = targetNode;
  969.                         XmlDiffNode targetClosestMatchNode = sourceNode;
  970.                         XmlDiffOperation sourceOp = op;
  971.                         XmlDiffOperation targetOp = op;
  972.                         int sourceNodesToAddCount = targetNode.NodesCount;
  973.                         int targetNodesToRemoveCount = sourceNode.NodesCount;
  974.                         XmlDiffNode nextSourceNode = sourceNode._nextSibling;
  975.                         XmlDiffNode nextTargetNode = targetNode._nextSibling;
  976.                         // walk limited number of siblings to find the closest matching node
  977.                         for ( int i = 0; i < walkNodesCount && ( nextTargetNode != null || nextSourceNode != null ); i++ ) {
  978.                             if ( nextTargetNode != null && sourceOp != XmlDiffOperation.Match ) {
  979.                                 XmlDiffOperation o = sourceNode.GetDiffOperation( nextTargetNode, _xmlDiff );
  980.                                 if ( MinimalTreeDistanceAlgo.OperationCost[(int)o] < MinimalTreeDistanceAlgo.OperationCost[(int)sourceOp] ) {
  981.                                     sourceOp = o;
  982.                                     sourceClosestMatchNode = nextTargetNode;
  983.                                 }
  984.                                 else {
  985.                                     sourceNodesToAddCount += nextTargetNode.NodesCount;
  986.                                     nextTargetNode = nextTargetNode._nextSibling;
  987.                                 }
  988.                             }
  989.                             if ( nextSourceNode != null && targetOp != XmlDiffOperation.Match ) {
  990.                                 XmlDiffOperation o = targetNode.GetDiffOperation( nextSourceNode, _xmlDiff );
  991.                                 if ( MinimalTreeDistanceAlgo.OperationCost[(int)o] < MinimalTreeDistanceAlgo.OperationCost[(int)targetOp] ) {
  992.                                     targetOp = o;
  993.                                     targetClosestMatchNode = nextSourceNode;
  994.                                 }
  995.                                 else {
  996.                                     targetNodesToRemoveCount += nextSourceNode.NodesCount;
  997.                                     nextSourceNode = nextSourceNode._nextSibling;
  998.                                 }
  999.                             }
  1000.                             if ( sourceOp == XmlDiffOperation.Match || targetOp == XmlDiffOperation.Match ) {
  1001.                                 break;
  1002.                             }
  1003.                             if ( _xmlDiff.IgnoreChildOrder ) {
  1004.                                 if ( nextTargetNode != null ) {
  1005.                                     if ( XmlDiffDocument.OrderChildren( sourceNode, nextTargetNode ) < 0 ) {
  1006.                                         nextTargetNode = null;
  1007.                                     }
  1008.                                 }
  1009.                                 if ( nextSourceNode != null ) {
  1010.                                     if ( XmlDiffDocument.OrderChildren( targetNode, nextSourceNode ) < 0 ) {
  1011.                                         nextSourceNode = null;
  1012.                                     }
  1013.                                 }
  1014.                             }
  1015.                         }
  1016.                         // source match exists and is better
  1017.                         if ( sourceOp == XmlDiffOperation.Match ) {
  1018.                             if ( targetOp != XmlDiffOperation.Match || sourceNodesToAddCount < targetNodesToRemoveCount ) {
  1019.                                 while ( targetNode != sourceClosestMatchNode ) {
  1020.                                     WalkTreeOnAddNode( diffParent, targetNode, needPositionSourceNode );
  1021.                                     needPositionSourceNode = null;
  1022.                                     targetNode = targetNode._nextSibling;
  1023.                                 }
  1024.                                 WalkTreeOnMatchNode( diffParent, sourceNode, targetNode, ref needPositionSourceNode ); 
  1025.                                 goto MoveToNextPair;
  1026.                             }
  1027.                         }
  1028.                         // target match exists and is better
  1029.                         else if ( targetOp == XmlDiffOperation.Match ) {
  1030.                             while ( sourceNode != targetClosestMatchNode ) {
  1031.                                 WalkTreeOnRemoveNode( diffParent, sourceNode );
  1032.                                 sourceNode = sourceNode._nextSibling;
  1033.                             }
  1034.                             needPositionSourceNode = null;
  1035.                             WalkTreeOnMatchNode( diffParent, sourceNode, targetNode, ref needPositionSourceNode );
  1036.                             goto MoveToNextPair;
  1037.                         }
  1038.                         // partial match
  1039.                         else {
  1040.                             Debug.Assert( sourceOp != XmlDiffOperation.Match && targetOp != XmlDiffOperation.Match );
  1041.                             int sourceOpCost = MinimalTreeDistanceAlgo.OperationCost[ (int)sourceOp ];
  1042.                             int targetOpCost = MinimalTreeDistanceAlgo.OperationCost[ (int)targetOp ];
  1043.                             if ( sourceOpCost < targetOpCost || ( sourceOpCost == targetOpCost && sourceNodesToAddCount < targetNodesToRemoveCount ) ) {
  1044.                                 while ( targetNode != sourceClosestMatchNode ) {
  1045.                                     WalkTreeOnAddNode( diffParent, targetNode, needPositionSourceNode );
  1046.                                     needPositionSourceNode = null;
  1047.                                     targetNode = targetNode._nextSibling;
  1048.                                 }
  1049.                                 op = sourceOp;
  1050.                             }
  1051.                             else {
  1052.                                 while ( sourceNode != targetClosestMatchNode ) {
  1053.                                     WalkTreeOnRemoveNode( diffParent, sourceNode );
  1054.                                     sourceNode = sourceNode._nextSibling;
  1055.                                 }
  1056.                                 op = targetOp;
  1057.                             }
  1058.                         }
  1059.                         // decide whether do 'change' or 'add / delete'
  1060.                         switch ( op ) {
  1061.                             case XmlDiffOperation.ChangeElementName:
  1062.                                 WalkTreeOnChangeElement( diffParent, (XmlDiffElement)sourceNode, (XmlDiffElement)targetNode, op );
  1063.                                 needPositionSourceNode = null;
  1064.                                 break;
  1065.                             case XmlDiffOperation.ChangeElementAttr1:
  1066.                             case XmlDiffOperation.ChangeElementAttr2:
  1067.                             case XmlDiffOperation.ChangeElementAttr3:
  1068.                             case XmlDiffOperation.ChangeElementNameAndAttr1:
  1069.                             case XmlDiffOperation.ChangeElementNameAndAttr2:
  1070.                             case XmlDiffOperation.ChangeElementNameAndAttr3:
  1071.                                 if ( GoForElementChange( (XmlDiffElement)sourceNode, (XmlDiffElement)targetNode ) ) {
  1072.                                     WalkTreeOnChangeElement( diffParent, (XmlDiffElement) sourceNode, (XmlDiffElement)targetNode, op );
  1073.                                     needPositionSourceNode = null;
  1074.                                 }
  1075.                                 else {
  1076.                                     goto case XmlDiffOperation.Undefined;
  1077.                                 }
  1078.                                 break;
  1079.                             case XmlDiffOperation.ChangePI:
  1080.                             case XmlDiffOperation.ChangeER:
  1081.                             case XmlDiffOperation.ChangeCharacterData:
  1082.                             case XmlDiffOperation.ChangeXmlDeclaration:
  1083.                             case XmlDiffOperation.ChangeDTD:
  1084.                                 diffParent.InsertAtEnd( new DiffgramChangeNode( sourceNode, targetNode, op, 0 ) );
  1085.                                 needPositionSourceNode = null;
  1086.                                 break;
  1087.                             case XmlDiffOperation.Undefined:
  1088.                                 // Prefer inserts against removes
  1089.                                 WalkTreeOnAddNode( diffParent, targetNode, needPositionSourceNode );
  1090.                                 needPositionSourceNode = null;
  1091.                                 targetNode = targetNode._nextSibling;
  1092.                                 continue;
  1093.                             default:
  1094.                                 Debug.Assert( false );
  1095.                                 break;
  1096.                         }
  1097.                     }
  1098.                 MoveToNextPair:
  1099.                     sourceNode = sourceNode._nextSibling;
  1100.                     targetNode = targetNode._nextSibling;
  1101.                 }
  1102.                 else { // targetNode == null
  1103.                     do {
  1104.                         WalkTreeOnRemoveNode( diffParent, sourceNode );
  1105.                         sourceNode = sourceNode._nextSibling;
  1106.                     } while ( sourceNode != null );
  1107.                 }
  1108.             } 
  1109.             else { // sourceNode == null
  1110.                 Debug.Assert( targetNode != null );
  1111.                 while ( targetNode != null ) {
  1112.                     WalkTreeOnAddNode( diffParent, targetNode, needPositionSourceNode );
  1113.                     needPositionSourceNode = null;
  1114.                     targetNode = targetNode._nextSibling;
  1115.                 }
  1116.             }
  1117.         }
  1118.     }
  1119.     // returns true if the two elements have at least 50% in common (common name & attributes)
  1120.     private bool GoForElementChange( XmlDiffElement sourceElement, XmlDiffElement targetElement ) {
  1121.         int identicalAttrCount = 0;
  1122.         int addedAttrCount = 0;
  1123.         int removedAttrCount = 0;
  1124.         int changedAttrCount = 0;
  1125.         bool bNameChange;
  1126.         bNameChange = ( sourceElement.LocalName != targetElement.LocalName );
  1127.         XmlDiffAttributeOrNamespace sourceAttr = sourceElement._attributes;
  1128.         XmlDiffAttributeOrNamespace targetAttr = targetElement._attributes;
  1129.         while ( sourceAttr != null && targetAttr != null ) {
  1130.             if ( sourceAttr.LocalName == targetAttr.LocalName ) {
  1131.                 if ( ( _xmlDiff.IgnorePrefixes || _xmlDiff.IgnoreNamespaces || sourceAttr.Prefix == targetAttr.Prefix ) &&
  1132.                      ( _xmlDiff.IgnoreNamespaces || sourceAttr.NamespaceURI == targetAttr.NamespaceURI ) ) {
  1133.                     if ( sourceAttr.Value == targetAttr.Value ) {
  1134.                         identicalAttrCount++;
  1135.                     }
  1136.                     else {
  1137.                         changedAttrCount++;
  1138.                     }
  1139.                 }
  1140.                 else {
  1141.                     changedAttrCount++;
  1142.                 }
  1143.                 sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  1144.                 targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  1145.             }
  1146.             else {
  1147.                 int compare = XmlDiffDocument.OrderAttributesOrNamespaces( sourceAttr, targetAttr );
  1148.                 if ( compare < 0 ) {
  1149.                     removedAttrCount++;
  1150.                     sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  1151.                 }
  1152.                 else {
  1153.                     addedAttrCount++;
  1154.                     targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  1155.                 }
  1156.             }
  1157.         }
  1158.         while ( sourceAttr != null ) {
  1159.             removedAttrCount++;
  1160.             sourceAttr = (XmlDiffAttributeOrNamespace)sourceAttr._nextSibling;
  1161.         }
  1162.         while ( targetAttr != null ) {
  1163.             addedAttrCount++;
  1164.             targetAttr = (XmlDiffAttributeOrNamespace)targetAttr._nextSibling;
  1165.         }
  1166.         if ( bNameChange ) {
  1167.             // total number of changes is less than 50%
  1168.             if ( removedAttrCount + addedAttrCount + changedAttrCount <= identicalAttrCount )
  1169.                 return true;
  1170.             return false;
  1171.         }
  1172.         else {
  1173.             // only added
  1174.             if ( removedAttrCount + changedAttrCount == 0 )
  1175.                 return true;
  1176.             // only removed
  1177.             if ( addedAttrCount + changedAttrCount == 0 )
  1178.                 return true;
  1179.             // no removed or added: 
  1180.             if ( removedAttrCount + addedAttrCount == 0 ) {
  1181.                 return true;
  1182.             }
  1183.             // total number of changes is less than 75% - or - 
  1184.             // no other sibling node
  1185.             if ( removedAttrCount + addedAttrCount + changedAttrCount <= identicalAttrCount * 3 ||
  1186.                 sourceElement._nextSibling == null )
  1187.                 return true;
  1188.             return false;
  1189.         }
  1190.     }
  1191.     private void WalkTreeOnRemoveNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode ) {
  1192.         bool bShrankNode = sourceNode is XmlDiffShrankNode;
  1193.         if ( sourceNode._bSomeDescendantMatches && !bShrankNode )
  1194.         {
  1195.             DiffgramOperation removeOp = GenerateDiffgramRemoveWhenDescendantMatches( (XmlDiffParentNode)sourceNode );
  1196.             diffParent.InsertAtEnd( removeOp );
  1197.         }
  1198.         else
  1199.         {
  1200. ulong opid = 0;
  1201.             // shrank node -> output as 'move' operation
  1202. if ( bShrankNode )
  1203. {
  1204. XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) sourceNode;
  1205. if ( shrankNode.MoveOperationId == 0 )
  1206. shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  1207. opid = shrankNode.MoveOperationId;
  1208. }
  1209. if ( opid != 0 ||
  1210. !diffParent.MergeRemoveSubtreeAtEnd( sourceNode ) )
  1211. {
  1212. diffParent.InsertAtEnd( new DiffgramRemoveSubtrees( sourceNode, opid, !_xmlDiff.IgnoreChildOrder ) );
  1213. }
  1214.         }
  1215.     }
  1216.     private void WalkTreeOnAddNode( DiffgramParentOperation diffParent, XmlDiffNode targetNode, XmlDiffNode sourcePositionNode ) {
  1217.         bool bShrankNode = targetNode is XmlDiffShrankNode;
  1218.         if ( _bChildOrderSignificant ) {
  1219.             if ( sourcePositionNode != null ) {
  1220.                 diffParent.InsertAtEnd( new DiffgramPosition( sourcePositionNode ) );
  1221.             }
  1222.         }
  1223.         else {
  1224.             if ( diffParent._firstChildOp == null && diffParent is Diffgram ) {
  1225.                 diffParent.InsertAtEnd( new DiffgramPosition( sourcePositionNode ) );
  1226.             }
  1227.         }
  1228.         
  1229.         if ( targetNode._bSomeDescendantMatches && !bShrankNode ) {
  1230.             DiffgramOperation addOp = GenerateDiffgramAddWhenDescendantMatches( (XmlDiffParentNode)targetNode );
  1231.             diffParent.InsertAtEnd( addOp );
  1232.         }
  1233.         else {
  1234.             // shrank node -> output as 'move' operation
  1235.             if ( bShrankNode ) {
  1236.                 ulong opid = 0;
  1237.                 XmlDiffShrankNode shrankNode = (XmlDiffShrankNode) targetNode;
  1238.                 if ( shrankNode.MoveOperationId == 0 )
  1239.                     shrankNode.MoveOperationId = GenerateOperationID( XmlDiffDescriptorType.Move );
  1240.                 opid = shrankNode.MoveOperationId;
  1241.                 diffParent.InsertAtEnd( new DiffgramCopy( shrankNode.MatchingShrankNode, true, opid ) );
  1242.             }
  1243.             else {
  1244.                 switch ( targetNode.NodeType ) {
  1245.                     case XmlDiffNodeType.XmlDeclaration:
  1246.                     case XmlDiffNodeType.DocumentType:
  1247.                     case XmlDiffNodeType.EntityReference:
  1248.          diffParent.InsertAtEnd( new DiffgramAddNode( targetNode, 0 ) );
  1249.                         break;
  1250.                     default:
  1251.                         if ( !diffParent.MergeAddSubtreeAtEnd( targetNode ) ) {
  1252.                             diffParent.InsertAtEnd( new DiffgramAddSubtrees( targetNode, 0, !_xmlDiff.IgnoreChildOrder ) );
  1253.                         }
  1254.                         break;
  1255.                 }
  1256.             }
  1257.         }
  1258.     }
  1259.     private void WalkTreeOnChangeNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode, XmlDiffNode targetNode, XmlDiffOperation op ) {
  1260.         Debug.Assert( sourceNode.NodeType != XmlDiffNodeType.Element && targetNode.NodeType != XmlDiffNodeType.Element );
  1261.         DiffgramChangeNode changeOp = new DiffgramChangeNode( sourceNode, targetNode, op, 0 );
  1262.         if ( sourceNode.HasChildNodes || targetNode.HasChildNodes ) {
  1263.             WalkTreeGenerateDiffgramMatch( changeOp, (XmlDiffParentNode) sourceNode, (XmlDiffParentNode) targetNode );
  1264.         }
  1265.         diffParent.InsertAtEnd( changeOp );
  1266.     }
  1267.     private void WalkTreeOnChangeElement( DiffgramParentOperation diffParent, XmlDiffElement sourceElement, XmlDiffElement targetElement, XmlDiffOperation op ) {
  1268.         DiffgramParentOperation diffOp;
  1269.         if ( XmlDiff.IsChangeOperationOnAttributesOnly( op ) ) {
  1270.             diffOp = new DiffgramPosition( sourceElement );
  1271.         }
  1272.         else
  1273.         {
  1274.             ulong opid = 0;
  1275.             if ( !_xmlDiff.IgnoreNamespaces && sourceElement.LocalName == targetElement.LocalName)
  1276.             {
  1277.                 opid = GetNamespaceChangeOpid( sourceElement.NamespaceURI, sourceElement.Prefix,
  1278.                                                targetElement.NamespaceURI, targetElement.Prefix );
  1279.             }
  1280.             Debug.Assert( (int)op >= (int)XmlDiffOperation.ChangeElementName &&
  1281.                           (int)op <= (int)XmlDiffOperation.ChangeElementNameAndAttr3 );
  1282.             diffOp = new DiffgramChangeNode( sourceElement, targetElement, XmlDiffOperation.ChangeElementName, opid );
  1283.         }
  1284.         GenerateChangeDiffgramForAttributes( diffOp, sourceElement, targetElement );
  1285.         if ( sourceElement.HasChildNodes || targetElement.HasChildNodes ) {
  1286.             WalkTreeGenerateDiffgramMatch( diffOp, sourceElement, targetElement );
  1287.         }
  1288.         diffParent.InsertAtEnd( diffOp );
  1289.     }
  1290.     private void WalkTreeOnMatchNode( DiffgramParentOperation diffParent, XmlDiffNode sourceNode, XmlDiffNode targetNode, ref XmlDiffNode needPositionSourceNode ) {
  1291.         if ( sourceNode.HasChildNodes || targetNode.HasChildNodes ) {
  1292.             DiffgramPosition diffMatch = new DiffgramPosition( sourceNode, targetNode );
  1293.             WalkTreeGenerateDiffgramMatch( diffMatch, (XmlDiffParentNode)sourceNode, (XmlDiffParentNode)targetNode );
  1294.             diffParent.InsertAtEnd( diffMatch );
  1295.             needPositionSourceNode = null;
  1296.         }
  1297.         else {
  1298.             if ( sourceNode.NodeType == XmlDiffNodeType.ShrankNode ) {
  1299.                 needPositionSourceNode = ((XmlDiffShrankNode)sourceNode)._lastNode;
  1300.             }
  1301.             else {
  1302.                 needPositionSourceNode = sourceNode;
  1303.             }
  1304.         }
  1305.     }
  1306.     private bool ParentMatches( XmlDiffNode sourceNode, XmlDiffNode targetNode, DiffgramOperation diffParent ) {
  1307.         switch ( diffParent.Operation ) {
  1308.             case XmlDiffOperation.Match:
  1309.                 DiffgramPosition diffMatch = diffParent as DiffgramPosition;
  1310.                 if ( diffMatch != null ) {
  1311.                     return ( diffMatch._sourceNode == sourceNode._parent && 
  1312.                              diffMatch._targetNode == targetNode._parent );
  1313.                 }
  1314.                 return false;
  1315.             case XmlDiffOperation.Remove:
  1316.                 DiffgramRemoveNode diffRemove = diffParent as DiffgramRemoveNode;
  1317.                 if ( diffRemove != null && diffRemove._sourceNode == sourceNode._parent ) {
  1318.                     return ParentMatches( diffRemove._sourceNode, targetNode, diffParent._parent );
  1319.                 }
  1320.                 return false;
  1321.             case XmlDiffOperation.Add:
  1322.                 return false;
  1323.             default:
  1324.                 DiffgramChangeNode diffChange = diffParent as DiffgramChangeNode;
  1325.                 if ( diffChange != null ) {
  1326.                     return ( diffChange._sourceNode == sourceNode._parent &&
  1327.                              diffChange._targetNode == targetNode._parent );
  1328.                 }
  1329.                 return false;
  1330.         }
  1331.     }
  1332. }
  1333. }