XmlPatch.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:20k
源码类别:
xml/soap/webservice
开发平台:
Visual C++
- //------------------------------------------------------------------------------
- // <copyright file="XmlPatch.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- //------------------------------------------------------------------------------
- using System;
- using System.IO;
- using System.Xml;
- using System.Text;
- using System.Diagnostics;
- using Microsoft.XmlDiffPatch;
- namespace Microsoft.XmlDiffPatch
- {
- //////////////////////////////////////////////////////////////////
- // XmlPatch
- //
- /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch"]/*' />
- /// <summary>
- /// XML Patch modifies XML documents or nodes according to the XDL diffgram created by XML Diff.
- /// </summary>
- public class XmlPatch
- {
- // Fields
- XmlNode _sourceRootNode;
- bool _ignoreChildOrder;
- // Constructor
- public XmlPatch()
- {
- }
- // Methods
- /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch1"]/*' />
- /// <summary>
- /// Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
- /// sourceDoc according to the changes described in the diffgram.
- /// </summary>
- /// <param name="sourceDoc">The original xml document</param>
- /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
- public void Patch( XmlDocument sourceDoc, XmlReader diffgram )
- {
- if ( sourceDoc == null )
- throw new ArgumentNullException( "sourceDoc" );
- if ( diffgram == null )
- throw new ArgumentNullException( "diffgram" );
- XmlNode sourceNode = sourceDoc;
- Patch( ref sourceNode, diffgram );
- Debug.Assert( sourceNode == sourceDoc );
- }
- /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch3"]/*' />
- /// <summary>
- /// Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
- /// sourceDoc according to the changes described in the diffgram.
- /// </summary>
- /// <param name="sourceDoc">The original xml document</param>
- /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
- public void Patch( string sourceFile, Stream outputStream, XmlReader diffgram )
- {
- if ( sourceFile == null )
- throw new ArgumentNullException( "sourceFile" );
- if ( outputStream == null )
- throw new ArgumentNullException( "outputStream" );
- if ( diffgram == null )
- throw new ArgumentException( "diffgram" );
- XmlDocument diffDoc = new XmlDocument();
- diffDoc.Load( diffgram );
- // patch fragment
- if ( diffDoc.DocumentElement.GetAttribute( "fragments" ) == "yes" ) {
- NameTable nt = new NameTable();
- XmlTextReader tr = new XmlTextReader( new FileStream( sourceFile, FileMode.Open, FileAccess.Read ),
- XmlNodeType.Element,
- new XmlParserContext( nt, new XmlNamespaceManager( nt ),
- string.Empty, XmlSpace.Default ) );
- Patch( tr, outputStream, diffDoc );
- }
- // patch document
- else {
- Patch ( new XmlTextReader( sourceFile ), outputStream, diffDoc );
- }
- }
- /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch3"]/*' />
- /// <summary>
- /// Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
- /// sourceDoc according to the changes described in the diffgram.
- /// </summary>
- /// <param name="sourceDoc">The original xml document</param>
- /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
- public void Patch( XmlReader sourceReader, Stream outputStream, XmlReader diffgram ) {
- if ( sourceReader == null )
- throw new ArgumentNullException( "sourceReader" );
- if ( outputStream == null )
- throw new ArgumentNullException( "outputStream" );
- if ( diffgram == null )
- throw new ArgumentException( "diffgram" );
- XmlDocument diffDoc = new XmlDocument();
- diffDoc.Load( diffgram );
- Patch( sourceReader, outputStream, diffDoc );
- }
- private void Patch( XmlReader sourceReader, Stream outputStream, XmlDocument diffDoc ) {
- bool bFragments = diffDoc.DocumentElement.GetAttribute( "fragments" ) == "yes";
- Encoding enc = null;
- if ( bFragments ) {
- // load fragment
- XmlDocument tmpDoc = new XmlDocument();
- XmlDocumentFragment frag = tmpDoc.CreateDocumentFragment();
- XmlNode node;
- while ( ( node = tmpDoc.ReadNode( sourceReader ) ) != null ) {
- switch ( node.NodeType ) {
- case XmlNodeType.Whitespace:
- break;
- case XmlNodeType.XmlDeclaration:
- frag.InnerXml = node.OuterXml;
- break;
- default:
- frag.AppendChild( node );
- break;
- }
- if ( enc == null ) {
- if ( sourceReader is XmlTextReader ) {
- enc = ((XmlTextReader)sourceReader).Encoding;
- }
- else if ( sourceReader is XmlValidatingReader ) {
- enc = ((XmlValidatingReader)sourceReader).Encoding;
- }
- else {
- enc = Encoding.Unicode;
- }
- }
- }
- // patch
- XmlNode sourceNode = frag;
- Patch( ref sourceNode, diffDoc );
- Debug.Assert( sourceNode == frag );
- // save
- if ( frag.FirstChild != null && frag.FirstChild.NodeType == XmlNodeType.XmlDeclaration ) {
- enc = Encoding.GetEncoding( ((XmlDeclaration)sourceNode.FirstChild).Encoding );
- }
- XmlTextWriter tw = new XmlTextWriter( outputStream, enc );
- frag.WriteTo( tw );
- tw.Flush();
- }
- else {
- // load document
- XmlDocument sourceDoc = new XmlDocument();
- sourceDoc.XmlResolver = null;
- sourceDoc.Load( sourceReader );
- // patch
- XmlNode sourceNode = sourceDoc;
- Patch( ref sourceNode, diffDoc );
- Debug.Assert( sourceNode == sourceDoc );
- // save
- sourceDoc.Save( outputStream );
- }
- }
- /// <include file='docXmlPatch.uex' path='docs/doc[@for="XmlPatch.Patch2"]/*' />
- /// <summary>
- /// Reads the XDL diffgram from the diffgramFileName and modifies the original XML document
- /// sourceDoc according to the changes described in the diffgram.
- /// </summary>
- /// <param name="sourceDoc">The original xml node</param>
- /// <param name="diffgramFileName">XmlReader for the XDL diffgram.</param>
- public void Patch( ref XmlNode sourceNode, XmlReader diffgram )
- {
- if ( sourceNode == null )
- throw new ArgumentNullException( "sourceNode" );
- if ( diffgram == null )
- throw new ArgumentNullException( "diffgram" );
- XmlDocument diffDoc = new XmlDocument();
- diffDoc.Load( diffgram );
- Patch( ref sourceNode, diffDoc );
- }
- private void Patch( ref XmlNode sourceNode, XmlDocument diffDoc ) {
- XmlElement diffgramEl = diffDoc.DocumentElement;
- if ( diffgramEl.LocalName != "xmldiff" || diffgramEl.NamespaceURI != XmlDiff.NamespaceUri )
- XmlPatchError.Error( XmlPatchError.ExpectingDiffgramElement );
- XmlNamedNodeMap diffgramAttributes = diffgramEl.Attributes;
- XmlAttribute srcDocAttr = (XmlAttribute)diffgramAttributes.GetNamedItem( "srcDocHash" );
- if ( srcDocAttr == null )
- XmlPatchError.Error( XmlPatchError.MissingSrcDocAttribute );
- ulong hashValue = 0;
- try {
- hashValue = ulong.Parse( srcDocAttr.Value );
- }
- catch {
- XmlPatchError.Error( XmlPatchError.InvalidSrcDocAttribute );
- }
- XmlAttribute optionsAttr = (XmlAttribute) diffgramAttributes.GetNamedItem( "options" );
- if ( optionsAttr == null )
- XmlPatchError.Error( XmlPatchError.MissingOptionsAttribute );
- // parse options
- XmlDiffOptions xmlDiffOptions = XmlDiffOptions.None;
- try {
- xmlDiffOptions = XmlDiff.ParseOptions( optionsAttr.Value );
- }
- catch {
- XmlPatchError.Error( XmlPatchError.InvalidOptionsAttribute );
- }
- _ignoreChildOrder = ( (int)xmlDiffOptions & (int)XmlDiffOptions.IgnoreChildOrder ) != 0;
- // Calculate the hash value of source document and check if it agrees with
- // of srcDocHash attribute value.
- if ( !XmlDiff.VerifySource( sourceNode, hashValue, xmlDiffOptions ) )
- XmlPatchError.Error( XmlPatchError.SrcDocMismatch );
- // Translate diffgram & Apply patch
- if ( sourceNode.NodeType == XmlNodeType.Document )
- {
- Patch patch = CreatePatch( sourceNode, diffgramEl );
- // create temporary root element and move all document children under it
- XmlDocument sourceDoc = (XmlDocument)sourceNode;
- XmlElement tempRoot = sourceDoc.CreateElement( "tempRoot" );
- XmlNode child = sourceDoc.FirstChild;
- while ( child != null )
- {
- XmlNode tmpChild = child.NextSibling;
- if ( child.NodeType != XmlNodeType.XmlDeclaration &&
- child.NodeType != XmlNodeType.DocumentType )
- {
- sourceDoc.RemoveChild( child );
- tempRoot.AppendChild( child );
- }
- child = tmpChild;
- }
- sourceDoc.AppendChild( tempRoot );
- // Apply patch
- XmlNode temp = null;
- patch.Apply( tempRoot, ref temp );
- // remove the temporary root element
- if ( sourceNode.NodeType == XmlNodeType.Document ) {
- sourceDoc.RemoveChild( tempRoot );
- Debug.Assert( tempRoot.Attributes.Count == 0 );
- while ( ( child = tempRoot.FirstChild ) != null )
- {
- tempRoot.RemoveChild( child );
- sourceDoc.AppendChild( child );
- }
- }
- }
- else if ( sourceNode.NodeType == XmlNodeType.DocumentFragment ) {
- Patch patch = CreatePatch( sourceNode, diffgramEl );
- XmlNode temp = null;
- patch.Apply( sourceNode, ref temp );
- }
- else {
- // create fragment with sourceNode as its only child
- XmlDocumentFragment fragment = sourceNode.OwnerDocument.CreateDocumentFragment();
- XmlNode previousSourceParent = sourceNode.ParentNode;
- XmlNode previousSourceSibbling = sourceNode.PreviousSibling;
- if ( previousSourceParent != null ) {
- previousSourceParent.RemoveChild( sourceNode );
- }
- if ( sourceNode.NodeType != XmlNodeType.XmlDeclaration ) {
- fragment.AppendChild( sourceNode );
- }
- else {
- fragment.InnerXml = sourceNode.OuterXml;
- }
- Patch patch = CreatePatch( fragment, diffgramEl );
- XmlNode temp = null;
- patch.Apply( fragment, ref temp );
- XmlNodeList childNodes = fragment.ChildNodes;
- if ( childNodes.Count != 1 ) {
- XmlPatchError.Error( XmlPatchError.InternalErrorMoreThanOneNodeLeft, childNodes.Count.ToString() );
- }
- sourceNode = childNodes.Item(0);
- fragment.RemoveAll();
- if ( previousSourceParent != null ) {
- previousSourceParent.InsertAfter( sourceNode, previousSourceSibbling );
- }
- }
- }
- private Patch CreatePatch( XmlNode sourceNode, XmlElement diffgramElement )
- {
- Debug.Assert( sourceNode.NodeType == XmlNodeType.Document ||
- sourceNode.NodeType == XmlNodeType.DocumentFragment );
- Patch patch = new Patch( sourceNode );
- _sourceRootNode = sourceNode;
- // create patch for <xmldiff> node children
- CreatePatchForChildren( sourceNode,
- diffgramElement,
- patch );
- return patch;
- }
- private void CreatePatchForChildren( XmlNode sourceParent,
- XmlElement diffgramParent,
- XmlPatchParentOperation patchParent )
- {
- Debug.Assert( sourceParent != null );
- Debug.Assert( diffgramParent != null );
- Debug.Assert( patchParent != null );
- XmlPatchOperation lastPatchOp = null;
- XmlNode node = diffgramParent.FirstChild;
- while ( node != null )
- {
- if ( node.NodeType != XmlNodeType.Element ) {
- node = node.NextSibling;
- continue;
- }
- XmlElement diffOp = (XmlElement)node;
- XmlNodeList matchNodes = null;
- string matchAttr = diffOp.GetAttribute( "match" );
- if ( matchAttr != string.Empty )
- {
- matchNodes = PathDescriptorParser.SelectNodes( _sourceRootNode, sourceParent, matchAttr );
- if ( matchNodes.Count == 0 )
- XmlPatchError.Error( XmlPatchError.NoMatchingNode, matchAttr );
- }
- XmlPatchOperation patchOp = null;
- switch ( diffOp.LocalName )
- {
- case "node":
- {
- Debug.Assert( matchAttr != string.Empty );
- if ( matchNodes.Count != 1 )
- XmlPatchError.Error( XmlPatchError.MoreThanOneNodeMatched, matchAttr );
- XmlNode matchNode = matchNodes.Item( 0 );
- if ( _sourceRootNode.NodeType != XmlNodeType.Document ||
- ( matchNode.NodeType != XmlNodeType.XmlDeclaration && matchNode.NodeType != XmlNodeType.DocumentType ) ) {
- patchOp = new PatchSetPosition( matchNode );
- CreatePatchForChildren( matchNode, diffOp, (XmlPatchParentOperation) patchOp );
- }
- break;
- }
- case "add":
- {
- // copy node/subtree
- if ( matchAttr != string.Empty )
- {
- bool bSubtree = diffOp.GetAttribute( "subtree" ) != "no";
- patchOp = new PatchCopy( matchNodes, bSubtree );
- if ( !bSubtree )
- CreatePatchForChildren( sourceParent, diffOp, (XmlPatchParentOperation) patchOp );
- }
- else
- {
- string type = diffOp.GetAttribute( "type" );
- // add single node
- if ( type != string.Empty )
- {
- XmlNodeType nodeType = (XmlNodeType) int.Parse( type );
- bool bElement = (nodeType == XmlNodeType.Element);
- if ( nodeType != XmlNodeType.DocumentType ) {
- patchOp = new PatchAddNode( nodeType,
- diffOp.GetAttribute( "name" ),
- diffOp.GetAttribute( "ns" ),
- diffOp.GetAttribute( "prefix" ),
- bElement ? string.Empty : diffOp.InnerText,
- _ignoreChildOrder );
- if ( bElement )
- CreatePatchForChildren( sourceParent, diffOp, (XmlPatchParentOperation) patchOp );
- }
- else {
- patchOp = new PatchAddNode( nodeType,
- diffOp.GetAttribute( "name" ),
- diffOp.GetAttribute( "systemId" ),
- diffOp.GetAttribute( "publicId" ),
- diffOp.InnerText,
- _ignoreChildOrder );
- }
- }
- // add blob
- else
- {
- Debug.Assert( diffOp.ChildNodes.Count > 0 );
- patchOp = new PatchAddXmlFragment( diffOp.ChildNodes );
- }
- }
- break;
- }
- case "remove":
- {
- Debug.Assert( matchAttr != string.Empty );
- bool bSubtree = diffOp.GetAttribute( "subtree" ) != "no";
- patchOp = new PatchRemove( matchNodes, bSubtree );
- if ( !bSubtree )
- {
- Debug.Assert( matchNodes.Count == 1 );
- CreatePatchForChildren( matchNodes.Item(0), diffOp, (XmlPatchParentOperation) patchOp );
- }
- break;
- }
- case "change":
- {
- Debug.Assert( matchAttr != string.Empty );
- if ( matchNodes.Count != 1 )
- XmlPatchError.Error( XmlPatchError.MoreThanOneNodeMatched, matchAttr );
- XmlNode matchNode = matchNodes.Item( 0 );
- if ( matchNode.NodeType != XmlNodeType.DocumentType ) {
- patchOp = new PatchChange( matchNode,
- diffOp.HasAttribute( "name" ) ? diffOp.GetAttribute( "name" ) : null,
- diffOp.HasAttribute( "ns" ) ? diffOp.GetAttribute( "ns" ) : null,
- diffOp.HasAttribute( "prefix" ) ? diffOp.GetAttribute( "prefix" ) : null,
- (matchNode.NodeType == XmlNodeType.Element) ? null : diffOp );
- }
- else {
- patchOp = new PatchChange( matchNode,
- diffOp.HasAttribute( "name" ) ? diffOp.GetAttribute( "name" ) : null,
- diffOp.HasAttribute( "systemId" ) ? diffOp.GetAttribute( "systemId" ) : null,
- diffOp.HasAttribute( "publicId" ) ? diffOp.GetAttribute( "publicId" ) : null,
- diffOp.IsEmpty ? null : diffOp );
- }
- if ( matchNode.NodeType == XmlNodeType.Element )
- CreatePatchForChildren( matchNode, diffOp, (XmlPatchParentOperation) patchOp );
- break;
- }
- case "descriptor":
- return;
- default:
- Debug.Assert( false, "Invalid element in the XDL diffgram ." );
- break;
- }
- if ( patchOp != null ) {
- patchParent.InsertChildAfter( lastPatchOp, patchOp );
- lastPatchOp = patchOp;
- }
- node = node.NextSibling;
- }
- }
- }
- }