diff --git a/FatAntelope.Tests/XdtDiffWriterTests.cs b/FatAntelope.Tests/XdtDiffWriterTests.cs
index a8b147f..69bcad4 100644
--- a/FatAntelope.Tests/XdtDiffWriterTests.cs
+++ b/FatAntelope.Tests/XdtDiffWriterTests.cs
@@ -88,6 +88,64 @@ public void InsertBefore()
AssertCanTransform(source, target);
}
+ [TestMethod]
+ public void InsertFollowedByParentChange()
+ {
+ // Insert a child element into a parent element that has a change affecting its unique trait.
+
+ var source = @"
+
+
+
+
+
+
+ ";
+
+ var target = @"
+
+
+
+
+
+
+
+
+ ";
+
+ var patch = GetPatch(source, target);
+
+ AssertCanTransform(source, target);
+ }
+
+ [TestMethod]
+ public void InsertAndRemove()
+ {
+ // Insert a child element into a parent element that has a change affecting its unique trait.
+
+ var source = @"
+
+
+
+
+
+ ";
+
+ var target = @"
+
+
+
+
+
+
+
+ ";
+
+ var patch = GetPatch(source, target);
+
+ AssertCanTransform(source, target);
+ }
+
[TestMethod]
public void InsertChildWithParentChange()
{
@@ -181,14 +239,14 @@ public void InsertBeforeSingle()
var patch = GetPatch(source, target);
// Values
- AssertValue(patch.SelectSingleNode("/root/child[1]/@name"), "child1");
- AssertValue(patch.SelectSingleNode("/root/child[2]/@name"), "child2");
-
- // Transform = InsertBefore
- AssertTransform(patch.SelectSingleNode("/root/child[1]"), "InsertBefore(/root/*[1])");
+ AssertValue(patch.SelectSingleNode("/root/child[1]/@name"), "child2");
+ AssertValue(patch.SelectSingleNode("/root/child[2]/@name"), "child1");
// Transform = SetAttributes
- AssertTransform(patch.SelectSingleNode("/root/child[2]"), "SetAttributes(value)");
+ AssertTransform(patch.SelectSingleNode("/root/child[1]"), "SetAttributes(value)");
+
+ // Transform = InsertBefore
+ AssertTransform(patch.SelectSingleNode("/root/child[2]"), "InsertBefore(/root/*[1])");
AssertCanTransform(source, target);
}
diff --git a/FatAntelope/Writers/XdtDiffWriter.cs b/FatAntelope/Writers/XdtDiffWriter.cs
index 602dd6a..ec1bc22 100644
--- a/FatAntelope/Writers/XdtDiffWriter.cs
+++ b/FatAntelope/Writers/XdtDiffWriter.cs
@@ -226,23 +226,41 @@ private XmlNode WriteElement(XNode oldElement, XNode newElement, XmlNode target,
: null;
}
- // Process child elements
+ // Calculate xpath to current element for use in child node changes
path = GetPath(path, oldElement, oldTrait);
+
+ // Process 'changed' child elements first
for (var i = 0; i < newElement.Elements.Length; i++ )
{
var child = newElement.Elements[i];
- if (child.Match == MatchType.Change || child.Match == MatchType.NoMatch)
+ if (child.Match == MatchType.Change)
WriteElement(child.Matching, child, element, path, i);
}
-
- // Remove elements that dont exist in the target config
- // HACK: Reverse to remove from the end first, this prevents the condition when using
- // positional predicates to remove child[1] and then try also to remove child[2],
- // however, it has now moved up into position [1].
- foreach(var child in oldElement.Elements.Reverse())
+
+ // Process 'inserted' and 'removed' child elements together in reverse
+ var newElements = newElement.Elements;
+ var oldElements = oldElement.Elements;
+ var max = newElements.Length > oldElements.Length
+ ? newElements.Length
+ : oldElements.Length;
+
+ for (var i = max - 1; i >= 0; i--)
{
- if (child.Match == MatchType.NoMatch)
- WriteElement(child, null, element, path, 0);
+ // Remove child element
+ if(oldElements.Length > i)
+ {
+ var remove = oldElements[i];
+ if (remove.Match == MatchType.NoMatch)
+ WriteElement(remove, null, element, path, 0);
+ }
+
+ // Insert child element
+ if (newElements.Length > i)
+ {
+ var insert = newElements[i];
+ if (insert.Match == MatchType.NoMatch)
+ WriteElement(insert.Matching, insert, element, path, i);
+ }
}
return element;