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

xml/soap/webservice

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlPatch.cs" company="Microsoft">
  3. //     Copyright (c) Microsoft Corporation.  All rights reserved.
  4. // </copyright>                                                                
  5. //------------------------------------------------------------------------------
  6. using System;
  7. using System.IO;
  8. using System.Xml;
  9. using System.Text;
  10. using System.Diagnostics;
  11. using Microsoft.XmlDiffPatch;
  12. namespace Microsoft.XmlDiffPatch
  13. {
  14. //////////////////////////////////////////////////////////////////
  15. // XmlPatch
  16. //
  17. /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch"]/*' />
  18. /// <summary>
  19. ///    XML Patch modifies XML documents or nodes according to the XDL diffgram created by XML Diff.  
  20. /// </summary>
  21. public class XmlPatch
  22. {
  23. // Fields
  24.     XmlNode _sourceRootNode;
  25.     bool   _ignoreChildOrder;
  26. // Constructor
  27. public XmlPatch()
  28. {
  29. }
  30. // Methods
  31.     /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch1"]/*' />
  32.     /// <summary>
  33.     ///    Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
  34.     ///    sourceDoc according to the changes described in the diffgram. 
  35.     /// </summary>
  36.     /// <param name="sourceDoc">The original xml document</param>
  37.     /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
  38. public void Patch( XmlDocument sourceDoc, XmlReader diffgram ) 
  39. {
  40.         if ( sourceDoc == null )
  41.             throw new ArgumentNullException( "sourceDoc" );
  42.         if ( diffgram == null )
  43.             throw new ArgumentNullException( "diffgram" );
  44.         XmlNode sourceNode = sourceDoc;
  45. Patch( ref sourceNode, diffgram );
  46.         Debug.Assert( sourceNode == sourceDoc );
  47. }
  48.     
  49.     /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch3"]/*' />
  50.     /// <summary>
  51.     ///    Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
  52.     ///    sourceDoc according to the changes described in the diffgram. 
  53.     /// </summary>
  54.     /// <param name="sourceDoc">The original xml document</param>
  55.     /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
  56. public void Patch( string sourceFile, Stream outputStream, XmlReader diffgram ) 
  57. {
  58.         if ( sourceFile == null )
  59.             throw new ArgumentNullException( "sourceFile" );
  60.         if ( outputStream == null )
  61.             throw new ArgumentNullException( "outputStream" );
  62.         if ( diffgram == null ) 
  63.             throw new ArgumentException( "diffgram" );
  64.         XmlDocument diffDoc = new XmlDocument();
  65.         diffDoc.Load( diffgram );
  66.         // patch fragment
  67.         if ( diffDoc.DocumentElement.GetAttribute( "fragments" ) == "yes" ) {
  68.             NameTable nt = new NameTable();
  69.             XmlTextReader tr = new XmlTextReader( new FileStream( sourceFile, FileMode.Open, FileAccess.Read ),
  70.                                                   XmlNodeType.Element,
  71.                                                   new XmlParserContext( nt, new XmlNamespaceManager( nt ),
  72.                                                                         string.Empty, XmlSpace.Default ) );
  73.             Patch( tr, outputStream, diffDoc ); 
  74.         }
  75.         // patch document
  76.         else {
  77.             Patch ( new XmlTextReader( sourceFile ), outputStream, diffDoc );
  78.         }
  79. }
  80.     /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch3"]/*' />
  81.     /// <summary>
  82.     ///    Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
  83.     ///    sourceDoc according to the changes described in the diffgram. 
  84.     /// </summary>
  85.     /// <param name="sourceDoc">The original xml document</param>
  86.     /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
  87.     public void Patch( XmlReader sourceReader, Stream outputStream, XmlReader diffgram ) {
  88.         if ( sourceReader == null )
  89.             throw new ArgumentNullException( "sourceReader" );
  90.         if ( outputStream == null )
  91.             throw new ArgumentNullException( "outputStream" );
  92.         if ( diffgram == null ) 
  93.             throw new ArgumentException( "diffgram" );
  94.         XmlDocument diffDoc = new XmlDocument();
  95.         diffDoc.Load( diffgram );
  96.         Patch( sourceReader, outputStream, diffDoc );
  97.     }
  98.     private void Patch( XmlReader sourceReader, Stream outputStream, XmlDocument diffDoc ) {
  99.         bool bFragments = diffDoc.DocumentElement.GetAttribute( "fragments" ) == "yes"; 
  100.         Encoding enc = null;
  101.         if ( bFragments ) {
  102.             // load fragment
  103.             XmlDocument tmpDoc = new XmlDocument();
  104.             XmlDocumentFragment frag = tmpDoc.CreateDocumentFragment();
  105.             XmlNode node;
  106.             while ( ( node = tmpDoc.ReadNode( sourceReader ) ) != null ) {
  107.                 switch ( node.NodeType ) {
  108.                     case XmlNodeType.Whitespace:
  109.                         break;
  110.                     case XmlNodeType.XmlDeclaration:
  111.                         frag.InnerXml = node.OuterXml;
  112.                         break;
  113.                     default:
  114.                         frag.AppendChild( node );
  115.                         break;
  116.                 }
  117.                 if ( enc == null ) {
  118.                     if ( sourceReader is XmlTextReader ) {
  119.                         enc = ((XmlTextReader)sourceReader).Encoding;
  120.                     }
  121.                     else if ( sourceReader is XmlValidatingReader ) {
  122.                         enc = ((XmlValidatingReader)sourceReader).Encoding;
  123.                     }
  124.                     else {
  125.                         enc = Encoding.Unicode;
  126.                     }
  127.                 }
  128.             }
  129.             // patch
  130.             XmlNode sourceNode = frag;
  131.             Patch( ref sourceNode, diffDoc );
  132.             Debug.Assert( sourceNode == frag );
  133.             // save
  134.             if ( frag.FirstChild != null && frag.FirstChild.NodeType == XmlNodeType.XmlDeclaration ) {
  135.                 enc = Encoding.GetEncoding( ((XmlDeclaration)sourceNode.FirstChild).Encoding );
  136.             }
  137.             XmlTextWriter tw = new XmlTextWriter( outputStream, enc );
  138.             frag.WriteTo( tw );
  139.             tw.Flush();
  140.         }
  141.         else {
  142.             // load document
  143.             XmlDocument sourceDoc = new XmlDocument();
  144.             sourceDoc.XmlResolver = null;
  145.             sourceDoc.Load( sourceReader );
  146.             // patch
  147.             XmlNode sourceNode = sourceDoc;
  148.          Patch( ref sourceNode, diffDoc );
  149.             Debug.Assert( sourceNode == sourceDoc );
  150.             // save
  151.             sourceDoc.Save( outputStream );
  152.         }
  153.     }
  154.     /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch2"]/*' />
  155.     /// <summary>
  156.     ///    Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
  157.     ///    sourceDoc according to the changes described in the diffgram. 
  158.     /// </summary>
  159.     /// <param name="sourceDoc">The original xml node</param>
  160.     /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
  161.     public void Patch( ref XmlNode sourceNode, XmlReader diffgram )
  162.     {
  163.         if ( sourceNode == null )
  164.             throw new ArgumentNullException( "sourceNode" );
  165.         if ( diffgram == null )
  166.             throw new ArgumentNullException( "diffgram" );
  167.         XmlDocument diffDoc = new XmlDocument();
  168.         diffDoc.Load( diffgram );
  169.         Patch( ref sourceNode, diffDoc );
  170.     }
  171.     private void Patch( ref XmlNode sourceNode, XmlDocument diffDoc ) {
  172.         XmlElement diffgramEl = diffDoc.DocumentElement;
  173.         if ( diffgramEl.LocalName != "xmldiff" || diffgramEl.NamespaceURI != XmlDiff.NamespaceUri )
  174.             XmlPatchError.Error( XmlPatchError.ExpectingDiffgramElement );
  175.         XmlNamedNodeMap diffgramAttributes = diffgramEl.Attributes;
  176.         XmlAttribute srcDocAttr = (XmlAttribute)diffgramAttributes.GetNamedItem( "srcDocHash" );
  177.         if ( srcDocAttr == null )
  178.             XmlPatchError.Error( XmlPatchError.MissingSrcDocAttribute );
  179.         ulong hashValue = 0;
  180.         try { 
  181.             hashValue = ulong.Parse( srcDocAttr.Value );
  182.         }
  183.         catch {
  184.             XmlPatchError.Error( XmlPatchError.InvalidSrcDocAttribute );
  185.         }
  186.         XmlAttribute optionsAttr = (XmlAttribute) diffgramAttributes.GetNamedItem( "options" );
  187.         if ( optionsAttr == null )
  188.             XmlPatchError.Error( XmlPatchError.MissingOptionsAttribute );
  189.             
  190.         // parse options
  191.         XmlDiffOptions xmlDiffOptions = XmlDiffOptions.None;
  192.         try {
  193.             xmlDiffOptions = XmlDiff.ParseOptions( optionsAttr.Value );
  194.         }
  195.         catch {
  196.             XmlPatchError.Error( XmlPatchError.InvalidOptionsAttribute );
  197.         }
  198.         
  199.         _ignoreChildOrder = ( (int)xmlDiffOptions & (int)XmlDiffOptions.IgnoreChildOrder ) != 0;
  200.         // Calculate the hash value of source document and check if it agrees with
  201.         // of srcDocHash attribute value.
  202.         if ( !XmlDiff.VerifySource( sourceNode, hashValue, xmlDiffOptions ) )
  203.             XmlPatchError.Error( XmlPatchError.SrcDocMismatch );
  204.         // Translate diffgram & Apply patch
  205.         if ( sourceNode.NodeType == XmlNodeType.Document ) 
  206.         {
  207.             Patch patch = CreatePatch( sourceNode, diffgramEl );
  208.      // create temporary root element and move all document children under it
  209.             XmlDocument sourceDoc = (XmlDocument)sourceNode;
  210.      XmlElement tempRoot = sourceDoc.CreateElement( "tempRoot" );
  211.      XmlNode child = sourceDoc.FirstChild;
  212.      while ( child != null )
  213.      {
  214.      XmlNode tmpChild = child.NextSibling;
  215.     if ( child.NodeType != XmlNodeType.XmlDeclaration &&
  216.     child.NodeType != XmlNodeType.DocumentType )
  217.      {
  218.      sourceDoc.RemoveChild( child );
  219.      tempRoot.AppendChild( child );
  220.      }
  221.      child = tmpChild;
  222.      }
  223.      sourceDoc.AppendChild( tempRoot );
  224.             // Apply patch
  225.             XmlNode temp = null;
  226.             patch.Apply( tempRoot, ref temp );
  227.      // remove the temporary root element
  228.             if ( sourceNode.NodeType == XmlNodeType.Document ) {
  229.          sourceDoc.RemoveChild( tempRoot );
  230.           Debug.Assert( tempRoot.Attributes.Count == 0 );
  231.          while ( ( child = tempRoot.FirstChild ) != null )
  232.           {
  233.           tempRoot.RemoveChild( child );
  234.           sourceDoc.AppendChild( child );
  235.          }
  236.             }
  237.         }
  238.         else if ( sourceNode.NodeType == XmlNodeType.DocumentFragment ) {
  239.             Patch patch = CreatePatch( sourceNode, diffgramEl );
  240.             XmlNode temp = null;
  241.             patch.Apply( sourceNode, ref temp );
  242.         }
  243.         else {
  244.             // create fragment with sourceNode as its only child
  245.             XmlDocumentFragment fragment = sourceNode.OwnerDocument.CreateDocumentFragment();
  246.             XmlNode previousSourceParent = sourceNode.ParentNode;
  247.             XmlNode previousSourceSibbling = sourceNode.PreviousSibling;
  248.             if ( previousSourceParent != null ) {
  249.                 previousSourceParent.RemoveChild( sourceNode );
  250.             }
  251.             if ( sourceNode.NodeType != XmlNodeType.XmlDeclaration ) {
  252.                 fragment.AppendChild( sourceNode );
  253.             }
  254.             else {
  255.                 fragment.InnerXml = sourceNode.OuterXml;
  256.             }
  257.             Patch patch = CreatePatch( fragment, diffgramEl );
  258.             XmlNode temp = null;
  259.             patch.Apply( fragment, ref temp );
  260.             XmlNodeList childNodes = fragment.ChildNodes;
  261.             if ( childNodes.Count != 1 ) {
  262.                 XmlPatchError.Error( XmlPatchError.InternalErrorMoreThanOneNodeLeft, childNodes.Count.ToString() );
  263.             }
  264.             sourceNode = childNodes.Item(0);
  265.             fragment.RemoveAll();
  266.             if ( previousSourceParent != null ) {
  267.                 previousSourceParent.InsertAfter( sourceNode, previousSourceSibbling );
  268.             }
  269.         }
  270.     }
  271.     private Patch CreatePatch( XmlNode sourceNode, XmlElement diffgramElement )
  272. {
  273.         Debug.Assert( sourceNode.NodeType == XmlNodeType.Document ||
  274.                       sourceNode.NodeType == XmlNodeType.DocumentFragment );
  275.         Patch patch = new Patch( sourceNode );
  276.         _sourceRootNode = sourceNode;
  277.         // create patch for <xmldiff> node children
  278.         CreatePatchForChildren( sourceNode, 
  279.                                 diffgramElement,
  280.                                 patch );
  281.         return patch;
  282.     }
  283.     private void CreatePatchForChildren( XmlNode sourceParent, 
  284.                                          XmlElement diffgramParent, 
  285.                                          XmlPatchParentOperation patchParent )
  286.     {
  287.         Debug.Assert( sourceParent != null );
  288.         Debug.Assert( diffgramParent != null );
  289.         Debug.Assert( patchParent != null );
  290.         XmlPatchOperation lastPatchOp = null;
  291.         XmlNode node = diffgramParent.FirstChild;
  292.         while ( node != null )
  293.         {
  294.             if ( node.NodeType != XmlNodeType.Element ) {
  295.                 node = node.NextSibling;
  296.                 continue;
  297.             }
  298.             XmlElement diffOp = (XmlElement)node;
  299.             XmlNodeList matchNodes = null;
  300.             string matchAttr = diffOp.GetAttribute( "match" );
  301.             if ( matchAttr != string.Empty )
  302.             {
  303.                 matchNodes = PathDescriptorParser.SelectNodes( _sourceRootNode, sourceParent, matchAttr );
  304.                 
  305.                 if ( matchNodes.Count == 0 )
  306.                     XmlPatchError.Error( XmlPatchError.NoMatchingNode, matchAttr );
  307.             }
  308.             XmlPatchOperation patchOp = null;
  309.             switch ( diffOp.LocalName )
  310.             {
  311.                 case "node":
  312.                 {
  313.                     Debug.Assert( matchAttr != string.Empty );
  314.                     if ( matchNodes.Count != 1 )
  315.                         XmlPatchError.Error( XmlPatchError.MoreThanOneNodeMatched, matchAttr );
  316.                     XmlNode matchNode = matchNodes.Item( 0 );
  317.                     if ( _sourceRootNode.NodeType != XmlNodeType.Document ||
  318.                          ( matchNode.NodeType != XmlNodeType.XmlDeclaration && matchNode.NodeType != XmlNodeType.DocumentType ) ) {
  319.                         patchOp = new PatchSetPosition( matchNode );
  320.                         CreatePatchForChildren( matchNode, diffOp, (XmlPatchParentOperation) patchOp );
  321.                     }
  322.                     break;
  323.                 }
  324.                 case "add":
  325.                 {
  326.                     // copy node/subtree
  327.                     if ( matchAttr != string.Empty )
  328.                     {
  329.                         bool bSubtree = diffOp.GetAttribute( "subtree" ) != "no";
  330.                         patchOp = new PatchCopy( matchNodes, bSubtree );
  331.                         if ( !bSubtree )
  332.                             CreatePatchForChildren( sourceParent, diffOp, (XmlPatchParentOperation) patchOp );
  333.                     }
  334.                     else
  335.                     {
  336.                         string type = diffOp.GetAttribute( "type" );
  337.                         // add single node
  338.                         if ( type != string.Empty )
  339.                         {
  340.                             XmlNodeType nodeType = (XmlNodeType) int.Parse( type );
  341.                             bool bElement = (nodeType == XmlNodeType.Element);
  342.                             if ( nodeType != XmlNodeType.DocumentType ) {
  343.                                 patchOp = new PatchAddNode( nodeType,  
  344.                                                             diffOp.GetAttribute( "name" ), 
  345.                                                             diffOp.GetAttribute( "ns" ), 
  346.                                                             diffOp.GetAttribute( "prefix" ), 
  347.                                                             bElement ? string.Empty : diffOp.InnerText,
  348.                                                             _ignoreChildOrder );
  349.                                 if ( bElement )
  350.                                     CreatePatchForChildren( sourceParent, diffOp, (XmlPatchParentOperation) patchOp );
  351.                             }
  352.                             else {
  353.                                 patchOp = new PatchAddNode( nodeType,  
  354.                                                             diffOp.GetAttribute( "name" ), 
  355.                                                             diffOp.GetAttribute( "systemId" ), 
  356.                                                             diffOp.GetAttribute( "publicId" ), 
  357.                                                             diffOp.InnerText,
  358.                                                             _ignoreChildOrder );
  359.                             }
  360.                         }
  361.                         // add blob
  362.                         else
  363.                         {
  364.                             Debug.Assert( diffOp.ChildNodes.Count > 0 );
  365.                             patchOp = new PatchAddXmlFragment( diffOp.ChildNodes );
  366.                         }
  367.                     }
  368.                     break;
  369.                 }
  370.                 case "remove":
  371.                 {
  372.                     Debug.Assert( matchAttr != string.Empty );
  373.                     bool bSubtree = diffOp.GetAttribute( "subtree" ) != "no";
  374.                     patchOp = new PatchRemove( matchNodes, bSubtree );
  375.                     if ( !bSubtree )
  376.                     {
  377.                         Debug.Assert( matchNodes.Count == 1 );
  378.                         CreatePatchForChildren( matchNodes.Item(0), diffOp, (XmlPatchParentOperation) patchOp );
  379.                     }
  380.                     break;
  381.                 }
  382.                 case "change":
  383.                 {
  384.                     Debug.Assert( matchAttr != string.Empty );
  385.                     if ( matchNodes.Count != 1 )
  386.                         XmlPatchError.Error( XmlPatchError.MoreThanOneNodeMatched, matchAttr );
  387.                     XmlNode matchNode = matchNodes.Item( 0 );
  388.                     if ( matchNode.NodeType != XmlNodeType.DocumentType ) {
  389.                         patchOp = new PatchChange( matchNode, 
  390.                                                 diffOp.HasAttribute( "name" ) ? diffOp.GetAttribute( "name" ) : null,
  391.                                                 diffOp.HasAttribute( "ns" ) ? diffOp.GetAttribute( "ns" ) : null, 
  392.                                                 diffOp.HasAttribute( "prefix" ) ? diffOp.GetAttribute( "prefix" ) : null, 
  393.                                                 (matchNode.NodeType == XmlNodeType.Element) ? null : diffOp );
  394.                     }
  395.                     else {
  396.                         patchOp = new PatchChange( matchNode,
  397.                                                    diffOp.HasAttribute( "name" ) ? diffOp.GetAttribute( "name" ) : null, 
  398.                                                    diffOp.HasAttribute( "systemId" ) ? diffOp.GetAttribute( "systemId" ) : null,
  399.                                                    diffOp.HasAttribute( "publicId" ) ? diffOp.GetAttribute( "publicId" ) : null,
  400.                                                    diffOp.IsEmpty ? null : diffOp );
  401.                     }
  402.                     if ( matchNode.NodeType == XmlNodeType.Element )
  403.                         CreatePatchForChildren( matchNode, diffOp, (XmlPatchParentOperation) patchOp );
  404.                     break;
  405.                 }
  406.                 case "descriptor":
  407.                     return;
  408.                 default:
  409.                     Debug.Assert( false, "Invalid element in the XDL diffgram ." );
  410.                     break;
  411.             }
  412.             if ( patchOp != null ) {
  413.                 patchParent.InsertChildAfter( lastPatchOp, patchOp );
  414.                 lastPatchOp = patchOp;
  415.             }
  416.             node = node.NextSibling;
  417.         }
  418.     }
  419. }
  420. }