1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Text;
6 using System.IO;
7 using System.Diagnostics;
8 
9 [assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAssembly]
10 
11 namespace System.Xml.XmlDiff
12 {
13     internal enum DiffType
14     {
15         None,
16         Success,
17         Element,
18         Whitespace,
19         Comment,
20         PI,
21         Text,
22         CData,
23         Attribute,
24         NS,
25         Prefix,
26         SourceExtra,
27         TargetExtra,
28         NodeType
29     }
30 
31     public class XmlDiff
32     {
33         private XmlDiffDocument _SourceDoc;
34         private XmlDiffDocument _TargetDoc;
35 
36         private XmlWriter _Writer;     // Writer to write out the result
37         private StringBuilder _Output;
38 
39         // Option Flags
40         private XmlDiffOption _XmlDiffOption = XmlDiffOption.None;
41         private bool _IgnoreEmptyElement = true;
42         private bool _IgnoreWhitespace = true;
43         private bool _IgnoreComments = false;
44         private bool _IgnoreAttributeOrder = true;
45         private bool _IgnoreNS = true;
46         private bool _IgnorePrefix = true;
47         private bool _IgnoreDTD = true;
48         private bool _IgnoreChildOrder = true;
49 
XmlDiff()50         public XmlDiff()
51         {
52         }
53 
54         public XmlDiffOption Option
55         {
56             get
57             {
58                 return _XmlDiffOption;
59             }
60             set
61             {
62                 _XmlDiffOption = value;
63                 IgnoreEmptyElement = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreEmptyElement)) > 0;
64                 IgnoreWhitespace = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreWhitespace)) > 0;
65                 IgnoreComments = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreComments)) > 0;
66                 IgnoreAttributeOrder = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreAttributeOrder)) > 0;
67                 IgnoreNS = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreNS)) > 0;
68                 IgnorePrefix = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnorePrefix)) > 0;
69                 IgnoreDTD = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreDTD)) > 0;
70                 IgnoreChildOrder = (((int)_XmlDiffOption) & ((int)XmlDiffOption.IgnoreChildOrder)) > 0;
71             }
72         }
73 
74         internal bool IgnoreEmptyElement
75         {
76             get { return _IgnoreEmptyElement; }
77             set { _IgnoreEmptyElement = value; }
78         }
79 
80         internal bool IgnoreComments
81         {
82             get { return _IgnoreComments; }
83             set { _IgnoreComments = value; }
84         }
85 
86         internal bool IgnoreAttributeOrder
87         {
88             get { return _IgnoreAttributeOrder; }
89             set { _IgnoreAttributeOrder = value; }
90         }
91 
92         internal bool IgnoreWhitespace
93         {
94             get { return _IgnoreWhitespace; }
95             set { _IgnoreWhitespace = value; }
96         }
97 
98         internal bool IgnoreNS
99         {
100             get { return _IgnoreNS; }
101             set { _IgnoreNS = value; }
102         }
103 
104         internal bool IgnorePrefix
105         {
106             get { return _IgnorePrefix; }
107             set { _IgnorePrefix = value; }
108         }
109 
110         internal bool IgnoreDTD
111         {
112             get { return _IgnoreDTD; }
113             set { _IgnoreDTD = value; }
114         }
115 
116         internal bool IgnoreChildOrder
117         {
118             get { return _IgnoreChildOrder; }
119             set { _IgnoreChildOrder = value; }
120         }
121 
InitFiles()122         private void InitFiles()
123         {
124             this._SourceDoc = new XmlDiffDocument();
125             this._SourceDoc.Option = this._XmlDiffOption;
126             this._TargetDoc = new XmlDiffDocument();
127             this._TargetDoc.Option = this.Option;
128             _Output = new StringBuilder(String.Empty);
129         }
130 
Compare(Stream source, Stream target)131         public bool Compare(Stream source, Stream target)
132         {
133             InitFiles();
134 
135             this._SourceDoc.Load(XmlReader.Create(source));
136             this._TargetDoc.Load(XmlReader.Create(target));
137             return Diff();
138         }
139 
Compare(XmlReader source, XmlReader target)140         public bool Compare(XmlReader source, XmlReader target)
141         {
142             InitFiles();
143             this._SourceDoc.Load(source);
144             this._TargetDoc.Load(target);
145             return Diff();
146         }
147 
Compare(XmlReader source, XmlReader target, XmlDiffAdvancedOptions advOptions)148         public bool Compare(XmlReader source, XmlReader target, XmlDiffAdvancedOptions advOptions)
149         {
150             InitFiles();
151             this._SourceDoc.Load(source);
152             this._TargetDoc.Load(target);
153             XmlDiffNavigator nav = this._SourceDoc.CreateNavigator();
154             if (advOptions.IgnoreChildOrderExpr != null && advOptions.IgnoreChildOrderExpr != "")
155             {
156                 this._SourceDoc.SortChildren();
157                 this._TargetDoc.SortChildren();
158             }
159 
160             return Diff();
161         }
162 
Diff()163         private bool Diff()
164         {
165             bool flag = false;
166             XmlWriterSettings xws = new XmlWriterSettings();
167             xws.ConformanceLevel = ConformanceLevel.Auto;
168             xws.CheckCharacters = false;
169             _Writer = XmlWriter.Create(new StringWriter(_Output), xws);
170             _Writer.WriteStartElement(String.Empty, "Root", String.Empty);
171 
172             flag = CompareChildren(this._SourceDoc, this._TargetDoc);
173 
174             _Writer.WriteEndElement();
175             _Writer.Dispose();
176             return flag;
177         }
178 
179         //  This function is being called recursively to compare the children of a certain node.
180         //  When calling this function the navigator should be pointing at the parent node whose children
181         //  we wants to compare and return the navigator back to the same node before exiting from it.
CompareChildren(XmlDiffNode sourceNode, XmlDiffNode targetNode)182         private bool CompareChildren(XmlDiffNode sourceNode, XmlDiffNode targetNode)
183         {
184             XmlDiffNode sourceChild = sourceNode.FirstChild;
185             XmlDiffNode targetChild = targetNode.FirstChild;
186 
187             bool flag = true;
188             bool tempFlag = true;
189 
190             DiffType result;
191 
192             // Loop to compare all the child elements of the parent node
193             while (sourceChild != null || targetChild != null)
194             {
195                 //Console.WriteLine( (sourceChild!=null)?(sourceChild.NodeType.ToString()):"null" );
196                 //Console.WriteLine( (targetChild!=null)?(targetChild.NodeType.ToString()):"null" );
197                 if (sourceChild != null)
198                 {
199                     if (targetChild != null)
200                     {
201                         // Both Source and Target Read successful
202                         if ((result = CompareNodes(sourceChild, targetChild)) == DiffType.Success)
203                         {
204                             // Child nodes compared successfully, write out the result
205                             WriteResult(sourceChild, targetChild, DiffType.Success);
206                             // Check whether this Node has  Children, if it does call CompareChildren recursively
207                             if (sourceChild.FirstChild != null)
208                                 tempFlag = CompareChildren(sourceChild, targetChild);
209                             else if (targetChild.FirstChild != null)
210                             {
211                                 WriteResult(null, targetChild, DiffType.TargetExtra);
212                                 tempFlag = false;
213                             }
214                             // set the compare flag
215                             flag = (flag && tempFlag);
216 
217                             // Writing out End Element to the result
218                             if (sourceChild.NodeType == XmlDiffNodeType.Element && !(sourceChild is XmlDiffEmptyElement))
219                             {
220                                 XmlDiffElement sourceElem = sourceChild as XmlDiffElement;
221                                 XmlDiffElement targetElem = targetChild as XmlDiffElement;
222                                 Debug.Assert(sourceElem != null);
223                                 Debug.Assert(targetElem != null);
224                                 _Writer.WriteStartElement(String.Empty, "Node", String.Empty);
225                                 _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, sourceElem.EndLineNumber.ToString());
226                                 _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, sourceElem.EndLinePosition.ToString());
227                                 _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, targetElem.EndLineNumber.ToString());
228                                 _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, targetElem.EndLinePosition.ToString());
229                                 _Writer.WriteStartElement(String.Empty, "Diff", String.Empty);
230                                 _Writer.WriteEndElement();
231 
232                                 _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty);
233                                 _Writer.WriteCData("</" + sourceElem.Name + ">");
234                                 _Writer.WriteEndElement();
235                                 _Writer.WriteEndElement();
236                             }
237 
238                             // Move to Next child
239                             sourceChild = sourceChild.NextSibling;
240                             targetChild = targetChild.NextSibling;
241                         }
242                         else
243                         {
244                             // Child nodes not matched, start the recovery process
245                             bool recoveryFlag = false;
246 
247                             // Try to match the source node with the target nodes at the same level
248                             XmlDiffNode backupTargetChild = targetChild.NextSibling;
249                             while (!recoveryFlag && backupTargetChild != null)
250                             {
251                                 if (CompareNodes(sourceChild, backupTargetChild) == DiffType.Success)
252                                 {
253                                     recoveryFlag = true;
254                                     do
255                                     {
256                                         WriteResult(null, targetChild, DiffType.TargetExtra);
257                                         targetChild = targetChild.NextSibling;
258                                     } while (targetChild != backupTargetChild);
259                                     break;
260                                 }
261                                 backupTargetChild = backupTargetChild.NextSibling;
262                             }
263 
264                             // If not recovered, try to match the target node with the source nodes at the same level
265                             if (!recoveryFlag)
266                             {
267                                 XmlDiffNode backupSourceChild = sourceChild.NextSibling;
268                                 while (!recoveryFlag && backupSourceChild != null)
269                                 {
270                                     if (CompareNodes(backupSourceChild, targetChild) == DiffType.Success)
271                                     {
272                                         recoveryFlag = true;
273                                         do
274                                         {
275                                             WriteResult(sourceChild, null, DiffType.SourceExtra);
276                                             sourceChild = sourceChild.NextSibling;
277                                         } while (sourceChild != backupSourceChild);
278                                         break;
279                                     }
280                                     backupSourceChild = backupSourceChild.NextSibling;
281                                 }
282                             }
283 
284                             // If still not recovered, write both of them as different nodes and move on
285                             if (!recoveryFlag)
286                             {
287                                 WriteResult(sourceChild, targetChild, result);
288 
289                                 // Check whether this Node has  Children, if it does call CompareChildren recursively
290                                 if (sourceChild.FirstChild != null)
291                                 {
292                                     tempFlag = CompareChildren(sourceChild, targetChild);
293                                 }
294                                 else if (targetChild.FirstChild != null)
295                                 {
296                                     WriteResult(null, targetChild, DiffType.TargetExtra);
297                                     tempFlag = false;
298                                 }
299 
300                                 // Writing out End Element to the result
301                                 bool bSourceNonEmpElemEnd = (sourceChild.NodeType == XmlDiffNodeType.Element && !(sourceChild is XmlDiffEmptyElement));
302                                 bool bTargetNonEmpElemEnd = (targetChild.NodeType == XmlDiffNodeType.Element && !(targetChild is XmlDiffEmptyElement));
303                                 if (bSourceNonEmpElemEnd || bTargetNonEmpElemEnd)
304                                 {
305                                     XmlDiffElement sourceElem = sourceChild as XmlDiffElement;
306                                     XmlDiffElement targetElem = targetChild as XmlDiffElement;
307                                     _Writer.WriteStartElement(String.Empty, "Node", String.Empty);
308                                     _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, (sourceElem != null) ? sourceElem.EndLineNumber.ToString() : "-1");
309                                     _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, (sourceElem != null) ? sourceElem.EndLinePosition.ToString() : "-1");
310                                     _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, (targetElem != null) ? targetElem.EndLineNumber.ToString() : "-1");
311                                     _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, (targetElem != null) ? targetElem.EndLineNumber.ToString() : "-1");
312                                     _Writer.WriteStartElement(String.Empty, "Diff", String.Empty);
313                                     _Writer.WriteAttributeString(String.Empty, "DiffType", String.Empty, GetDiffType(result));
314                                     if (bSourceNonEmpElemEnd)
315                                     {
316                                         _Writer.WriteStartElement(String.Empty, "File1", String.Empty);
317                                         _Writer.WriteCData("</" + sourceElem.Name + ">");
318                                         _Writer.WriteEndElement();
319                                     }
320 
321                                     if (bTargetNonEmpElemEnd)
322                                     {
323                                         _Writer.WriteStartElement(String.Empty, "File2", String.Empty);
324                                         _Writer.WriteCData("</" + targetElem.Name + ">");
325                                         _Writer.WriteEndElement();
326                                     }
327 
328                                     _Writer.WriteEndElement();
329 
330                                     _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty);
331                                     _Writer.WriteEndElement();
332                                     _Writer.WriteEndElement();
333                                 }
334 
335                                 sourceChild = sourceChild.NextSibling;
336                                 targetChild = targetChild.NextSibling;
337                             }
338 
339                             flag = false;
340                         }
341                     }
342                     else
343                     {
344                         // SourceRead NOT NULL targetRead is NULL
345                         WriteResult(sourceChild, null, DiffType.SourceExtra);
346                         flag = false;
347                         sourceChild = sourceChild.NextSibling;
348                     }
349                 }
350                 else if (targetChild != null)
351                 {
352                     // SourceRead is NULL and targetRead is NOT NULL
353                     WriteResult(null, targetChild, DiffType.TargetExtra);
354                     flag = false;
355                     targetChild = targetChild.NextSibling;
356                 }
357                 else
358                 {
359                     //Both SourceRead and TargetRead is NULL
360                     Debug.Assert(false, "Impossible Situation for comparison");
361                 }
362             }
363             return flag;
364         }
365 
366         // This function compares the two nodes passed to it, depending upon the options set by the user.
CompareNodes(XmlDiffNode sourceNode, XmlDiffNode targetNode)367         private DiffType CompareNodes(XmlDiffNode sourceNode, XmlDiffNode targetNode)
368         {
369             if (sourceNode.NodeType != targetNode.NodeType)
370             {
371                 return DiffType.NodeType;
372             }
373             switch (sourceNode.NodeType)
374             {
375                 case XmlDiffNodeType.Element:
376                     XmlDiffElement sourceElem = sourceNode as XmlDiffElement;
377                     XmlDiffElement targetElem = targetNode as XmlDiffElement;
378                     Debug.Assert(sourceElem != null);
379                     Debug.Assert(targetElem != null);
380                     if (!IgnoreNS)
381                     {
382                         if (sourceElem.NamespaceURI != targetElem.NamespaceURI)
383                             return DiffType.NS;
384                     }
385                     if (!IgnorePrefix)
386                     {
387                         if (sourceElem.Prefix != targetElem.Prefix)
388                             return DiffType.Prefix;
389                     }
390 
391                     if (sourceElem.LocalName != targetElem.LocalName)
392                         return DiffType.Element;
393                     if (!IgnoreEmptyElement)
394                     {
395                         if ((sourceElem is XmlDiffEmptyElement) != (targetElem is XmlDiffEmptyElement))
396                             return DiffType.Element;
397                     }
398                     if (!CompareAttributes(sourceElem, targetElem))
399                     {
400                         return DiffType.Attribute;
401                     }
402                     break;
403                 case XmlDiffNodeType.Text:
404                 case XmlDiffNodeType.Comment:
405                 case XmlDiffNodeType.WS:
406                     XmlDiffCharacterData sourceText = sourceNode as XmlDiffCharacterData;
407                     XmlDiffCharacterData targetText = targetNode as XmlDiffCharacterData;
408                     Debug.Assert(sourceText != null);
409                     Debug.Assert(targetText != null);
410                     if (IgnoreWhitespace)
411                     {
412                         if (sourceText.Value.Trim() == targetText.Value.Trim())
413                             return DiffType.Success;
414                     }
415                     else
416                     {
417                         if (sourceText.Value == targetText.Value)
418                             return DiffType.Success;
419                     }
420                     if (sourceText.NodeType == XmlDiffNodeType.Text || sourceText.NodeType == XmlDiffNodeType.WS)//should ws nodes also as text nodes???
421                         return DiffType.Text;
422                     else if (sourceText.NodeType == XmlDiffNodeType.Comment)
423                         return DiffType.Comment;
424                     else if (sourceText.NodeType == XmlDiffNodeType.CData)
425                         return DiffType.CData;
426                     else
427                         return DiffType.None;
428                 case XmlDiffNodeType.PI:
429                     XmlDiffProcessingInstruction sourcePI = sourceNode as XmlDiffProcessingInstruction;
430                     XmlDiffProcessingInstruction targetPI = targetNode as XmlDiffProcessingInstruction;
431                     Debug.Assert(sourcePI != null);
432                     Debug.Assert(targetPI != null);
433                     if (sourcePI.Name != targetPI.Name || sourcePI.Value != targetPI.Value)
434                         return DiffType.PI;
435                     break;
436                 default:
437                     break;
438             }
439 
440             return DiffType.Success;
441         }
442 
443         // This function writes the result in XML format so that it can be used by other applications to display the diff
WriteResult(XmlDiffNode sourceNode, XmlDiffNode targetNode, DiffType result)444         private void WriteResult(XmlDiffNode sourceNode, XmlDiffNode targetNode, DiffType result)
445         {
446             _Writer.WriteStartElement(String.Empty, "Node", String.Empty);
447             _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, (sourceNode != null) ? sourceNode.LineNumber.ToString() : "-1");
448             _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, (sourceNode != null) ? sourceNode.LinePosition.ToString() : "-1");
449             _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, (targetNode != null) ? targetNode.LineNumber.ToString() : "-1");
450             _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, (targetNode != null) ? targetNode.LinePosition.ToString() : "-1");
451             if (result == DiffType.Success)
452             {
453                 _Writer.WriteStartElement(String.Empty, "Diff", String.Empty);
454                 _Writer.WriteEndElement();
455 
456                 _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty);
457                 if (sourceNode.NodeType == XmlDiffNodeType.CData)
458                 {
459                     _Writer.WriteString("<![CDATA[");
460                     _Writer.WriteCData(GetNodeText(sourceNode, result));
461                     _Writer.WriteString("]]>");
462                 }
463                 else
464                 {
465                     _Writer.WriteCData(GetNodeText(sourceNode, result));
466                 }
467                 _Writer.WriteEndElement();
468             }
469             else
470             {
471                 _Writer.WriteStartElement(String.Empty, "Diff", String.Empty);
472                 _Writer.WriteAttributeString(String.Empty, "DiffType", String.Empty, GetDiffType(result));
473 
474                 if (sourceNode != null)
475                 {
476                     _Writer.WriteStartElement(String.Empty, "File1", String.Empty);
477                     if (sourceNode.NodeType == XmlDiffNodeType.CData)
478                     {
479                         _Writer.WriteString("<![CDATA[");
480                         _Writer.WriteCData(GetNodeText(sourceNode, result));
481                         _Writer.WriteString("]]>");
482                     }
483                     else
484                     {
485 
486                         _Writer.WriteString(GetNodeText(sourceNode, result));
487                     }
488                     _Writer.WriteEndElement();
489                 }
490 
491                 if (targetNode != null)
492                 {
493                     _Writer.WriteStartElement(String.Empty, "File2", String.Empty);
494                     if (targetNode.NodeType == XmlDiffNodeType.CData)
495                     {
496                         _Writer.WriteString("<![CDATA[");
497                         _Writer.WriteCData(GetNodeText(targetNode, result));
498                         _Writer.WriteString("]]>");
499                     }
500                     else
501                     {
502                         _Writer.WriteString(GetNodeText(targetNode, result));
503                     }
504                     _Writer.WriteEndElement();
505                 }
506 
507                 _Writer.WriteEndElement();
508 
509                 _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty);
510                 _Writer.WriteEndElement();
511             }
512             _Writer.WriteEndElement();
513         }
514 
515         // This is a helper function for WriteResult.  It gets the Xml representation of the different node we wants
516         // to write out and all it's children.
GetNodeText(XmlDiffNode diffNode, DiffType result)517         private String GetNodeText(XmlDiffNode diffNode, DiffType result)
518         {
519             string text = string.Empty;
520             switch (diffNode.NodeType)
521             {
522                 case XmlDiffNodeType.Element:
523                     if (result == DiffType.SourceExtra || result == DiffType.TargetExtra)
524                         return diffNode.OuterXml;
525                     StringWriter str = new StringWriter();
526                     XmlWriter writer = XmlWriter.Create(str);
527                     XmlDiffElement diffElem = diffNode as XmlDiffElement;
528                     Debug.Assert(diffNode != null);
529                     writer.WriteStartElement(diffElem.Prefix, diffElem.LocalName, diffElem.NamespaceURI);
530                     XmlDiffAttribute diffAttr = diffElem.FirstAttribute;
531                     while (diffAttr != null)
532                     {
533                         writer.WriteAttributeString(diffAttr.Prefix, diffAttr.LocalName, diffAttr.NamespaceURI, diffAttr.Value);
534                         diffAttr = (XmlDiffAttribute)diffAttr.NextSibling;
535                     }
536                     if (diffElem is XmlDiffEmptyElement)
537                     {
538                         writer.WriteEndElement();
539                         text = str.ToString();
540                     }
541                     else
542                     {
543                         text = str.ToString();
544                         text += ">";
545                     }
546                     writer.Dispose();
547                     break;
548                 case XmlDiffNodeType.CData:
549                     text = ((XmlDiffCharacterData)diffNode).Value;
550                     break;
551                 default:
552                     text = diffNode.OuterXml;
553                     break;
554             }
555             return text;
556         }
557 
558         // This function is used to compare the attributes of an element node according to the options set by the user.
CompareAttributes(XmlDiffElement sourceElem, XmlDiffElement targetElem)559         private bool CompareAttributes(XmlDiffElement sourceElem, XmlDiffElement targetElem)
560         {
561             Debug.Assert(sourceElem != null);
562             Debug.Assert(targetElem != null);
563             if (sourceElem.AttributeCount != targetElem.AttributeCount)
564                 return false;
565 
566             if (sourceElem.AttributeCount == 0)
567                 return true;
568 
569             XmlDiffAttribute sourceAttr = sourceElem.FirstAttribute;
570             XmlDiffAttribute targetAttr = targetElem.FirstAttribute;
571 
572             while (sourceAttr != null && targetAttr != null)
573             {
574                 if (!IgnoreNS)
575                 {
576                     if (sourceAttr.NamespaceURI != targetAttr.NamespaceURI)
577                         return false;
578                 }
579 
580                 if (!IgnorePrefix)
581                 {
582                     if (sourceAttr.Prefix != targetAttr.Prefix)
583                         return false;
584                 }
585 
586                 if (sourceAttr.LocalName != targetAttr.LocalName || sourceAttr.Value != targetAttr.Value)
587                     return false;
588                 sourceAttr = (XmlDiffAttribute)(sourceAttr.NextSibling);
589                 targetAttr = (XmlDiffAttribute)(targetAttr.NextSibling);
590             }
591             return true;
592         }
593 
ToXml()594         public string ToXml()
595         {
596             if (_Output != null)
597                 return _Output.ToString();
598             return string.Empty;
599         }
600 
ToXml(Stream stream)601         public void ToXml(Stream stream)
602         {
603             StreamWriter writer = new StreamWriter(stream);
604             writer.Write("<?xml-stylesheet type='text/xsl' href='diff.xsl'?>");
605             writer.Write(ToXml());
606             writer.Dispose();
607         }
608 
GetDiffType(DiffType type)609         private String GetDiffType(DiffType type)
610         {
611             switch (type)
612             {
613                 case DiffType.None:
614                     return String.Empty;
615                 case DiffType.Element:
616                     return "1";
617                 case DiffType.Whitespace:
618                     return "2";
619                 case DiffType.Comment:
620                     return "3";
621                 case DiffType.PI:
622                     return "4";
623                 case DiffType.Text:
624                     return "5";
625                 case DiffType.Attribute:
626                     return "6";
627                 case DiffType.NS:
628                     return "7";
629                 case DiffType.Prefix:
630                     return "8";
631                 case DiffType.SourceExtra:
632                     return "9";
633                 case DiffType.TargetExtra:
634                     return "10";
635                 case DiffType.NodeType:
636                     return "11";
637                 case DiffType.CData:
638                     return "12";
639                 default:
640                     return String.Empty;
641             }
642         }
643     }
644 }
645