Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Xml;

using Microsoft.Build.Construction;
using Shouldly;
using Xunit;

#nullable disable
Expand Down Expand Up @@ -70,6 +71,49 @@ public void ReadItemGroupTwoItems()
Assert.Equal("i2", items[1].Include);
}

[Fact]
public void DeepCopyFromItemGroupWithMetadata()
{
string content = @"
<Project>
<ItemGroup>
<i Include='i1'>
<M>metadataValue</M>
</i>
<i Include='i2'>
<M>
<Some>
<Xml With='Nesting' />
</Some>
</M>
</i>
</ItemGroup>
</Project>
";

ProjectRootElement project = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
ProjectItemGroupElement group = (ProjectItemGroupElement)Helpers.GetFirst(project.Children);

ProjectRootElement newProject = ProjectRootElement.Create();
ProjectItemGroupElement newItemGroup = project.AddItemGroup();

newItemGroup.DeepCopyFrom(group);

var items = Helpers.MakeList(newItemGroup.Items);

items.Count.ShouldBe(2);

items[0].Include.ShouldBe("i1");
ProjectMetadataElement metadataElement = items[0].Metadata.ShouldHaveSingleItem();
metadataElement.Name.ShouldBe("M");
metadataElement.Value.ShouldBe("metadataValue");

items[1].Include.ShouldBe("i2");
metadataElement = items[1].Metadata.ShouldHaveSingleItem();
metadataElement.Name.ShouldBe("M");
metadataElement.Value.ShouldBe("<Some><Xml With=\"Nesting\" /></Some>");
}

/// <summary>
/// Set the condition value
/// </summary>
Expand Down
35 changes: 34 additions & 1 deletion src/Build/Construction/ProjectElementContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,46 @@ protected internal virtual ProjectElementContainer DeepClone(ProjectRootElement
}
else
{
clone.AppendChild(child.Clone(clone.ContainingProject));
ProjectElement childClone = child.Clone(clone.ContainingProject);
clone.AppendChild(childClone);
if (childClone.XmlElement is not null)
{
AppendAttributesAndChildren(childClone.XmlElement, child.XmlElement);
}
}
}

return clone;
}

private void AppendAttributesAndChildren(XmlNode appendTo, XmlNode appendFrom)
{
appendTo.RemoveAll();
// Copy over the attributes from the template element.
if (appendFrom.Attributes is not null)
{
foreach (XmlAttribute attribute in appendFrom.Attributes)
{
XmlAttribute attr = appendTo.OwnerDocument.CreateAttribute(attribute.LocalName, attribute.NamespaceURI);
attr.Value = attribute.Value;
appendTo.Attributes.Append(attr);
}
}

// If this element has pure text content, copy that over.
if (appendFrom.ChildNodes.Count == 1 && appendFrom.FirstChild.NodeType == XmlNodeType.Text)
{
appendTo.AppendChild(appendTo.OwnerDocument.CreateTextNode(appendFrom.FirstChild.Value));
}

foreach (XmlNode child in appendFrom.ChildNodes)
{
XmlNode childClone = appendTo.OwnerDocument.CreateNode(child.NodeType, child.Prefix, child.Name, child.NamespaceURI);
appendTo.AppendChild(childClone);
AppendAttributesAndChildren(childClone, child);
}
}

internal static ProjectElementContainer DeepClone(ProjectElementContainer xml, ProjectRootElement factory, ProjectElementContainer parent)
{
return xml.DeepClone(factory, parent);
Expand Down