mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00

The bug caused a YSOD if the diffgram contained a match for a changed attribute being the last node of the diffgram.
112 lines
5.7 KiB
C#
112 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using Microsoft.XmlDiffPatch;
|
|
using Orchard.AuditTrail.Services.Models;
|
|
|
|
namespace Orchard.AuditTrail.Services {
|
|
public class DiffGramAnalyzer : IDiffGramAnalyzer {
|
|
public XElement GenerateDiffGram(XElement element1, XElement element2) {
|
|
using (var node1Reader = element1.CreateReader())
|
|
using (var node2Reader = element2.CreateReader()) {
|
|
var result = new XDocument();
|
|
using (var writer = result.CreateWriter()) {
|
|
var diff = new XmlDiff(XmlDiffOptions.IgnoreChildOrder | XmlDiffOptions.IgnoreWhitespace | XmlDiffOptions.IgnoreComments | XmlDiffOptions.IgnoreXmlDecl);
|
|
diff.Compare(node1Reader, node2Reader, writer);
|
|
writer.Flush();
|
|
writer.Close();
|
|
}
|
|
|
|
return result.Root;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<DiffNode> Analyze(XElement original, XElement diffGram) {
|
|
var stack = new Stack<XElement>();
|
|
|
|
stack.Push(new XElement("original", original));
|
|
|
|
using (var reader = diffGram.CreateReader()) {
|
|
while (!reader.EOF) {
|
|
var readNext = true;
|
|
switch (reader.NodeType) {
|
|
case XmlNodeType.Element:
|
|
var match = reader.GetAttribute("match");
|
|
var isAttributeChange = match != null && match.StartsWith("@");
|
|
var index = match == null || isAttributeChange ? default(int?) : Int32.Parse(match) - 1;
|
|
var diffType = reader.LocalName;
|
|
var currentElement = stack.Peek();
|
|
|
|
if (currentElement.HasElements && index != null) {
|
|
var sourceElement = currentElement.Elements().ElementAt(index.Value);
|
|
stack.Push(sourceElement);
|
|
}
|
|
|
|
if (diffType != "node") {
|
|
switch (diffType) {
|
|
case "change":
|
|
if (isAttributeChange) {
|
|
var attributeName = match.Substring(1);
|
|
var originalValue = currentElement.Attribute(attributeName).Value;
|
|
var currentValue = reader.ReadElementContentAsString();
|
|
|
|
readNext = false;
|
|
yield return
|
|
new DiffNode {
|
|
Type = DiffType.Change,
|
|
Context = BuildContextName(stack, attributeName),
|
|
Previous = originalValue,
|
|
Current = currentValue
|
|
};
|
|
}
|
|
else {
|
|
var elementName = currentElement.Name.ToString();
|
|
var originalContent = currentElement.Value;
|
|
var currentContent = reader.ReadElementContentAsString();
|
|
|
|
readNext = false;
|
|
yield return
|
|
new DiffNode {
|
|
Type = DiffType.Change,
|
|
Context = BuildContextName(stack, elementName),
|
|
Previous = originalContent,
|
|
Current = currentContent
|
|
};
|
|
}
|
|
break;
|
|
case "add":
|
|
var nodeName = reader.GetAttribute("name");
|
|
var addedContent = default(string);
|
|
reader.Read();
|
|
if (reader.NodeType != XmlNodeType.EndElement) {
|
|
nodeName = reader.Name;
|
|
addedContent = reader.ReadOuterXml();
|
|
}
|
|
yield return
|
|
new DiffNode {
|
|
Type = DiffType.Addition,
|
|
Context = BuildContextName(stack, nodeName),
|
|
Current = addedContent
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case XmlNodeType.EndElement:
|
|
if(stack.Any())
|
|
stack.Pop();
|
|
break;
|
|
}
|
|
if (readNext)
|
|
reader.Read();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string BuildContextName(IEnumerable<XElement> stack, string nodeName) {
|
|
return String.Format("{0}/{1}", String.Join("/", stack.Reverse().Skip(1).Select(x => x.Name)), nodeName);
|
|
}
|
|
}
|
|
} |