/// <summary> /// Sets the first child in this container /// </summary> private void AddInitialChild(ProjectElement child) { ErrorUtilities.VerifyThrow(FirstChild == null && LastChild == null, "Expecting no children"); VerifyForInsertBeforeAfterFirst(child, null); child.VerifyThrowInvalidOperationAcceptableLocation(this, null, null); child.Parent = this; FirstChild = child; LastChild = child; child.PreviousSibling = null; child.NextSibling = null; XmlElement.AppendChild(child.XmlElement); if (XmlDocument.PreserveWhitespace) { // If we are trying to preserve formatting of the file, then the new node won't automatically be indented. // So try to match the surrounding formatting and add one indentation level if (XmlElement.FirstChild.NodeType == XmlNodeType.Whitespace) { // This container had a whitespace node, which should generally be a newline and the indent // before the closing tag. So we add the default indentation to it so the child will now be indented // further, and then create a new whitespace node after the child so the closing tag will be on // a new line with the same indentation. // If the whitespace we end up copying isn't actually (newline + indentation) like we expect, then it // should still be OK to copy it, as we'll still be trying to match the surrounding formatting. string whitespace = XmlElement.FirstChild.Value; XmlElement.FirstChild.Value = whitespace + DEFAULT_INDENT; var newWhitespaceNode = XmlDocument.CreateWhitespace(whitespace); XmlElement.InsertAfter(newWhitespaceNode, child.XmlElement); } else if (XmlElement.PreviousSibling != null && XmlElement.PreviousSibling.NodeType == XmlNodeType.Whitespace) { // This container didn't have any whitespace in it. This probably means it didn't have separate open // and close tags. So add a whitespace node before the new child with additional indentation over the // container's indentation, and add a whitespace node with the same level of indentation as the container // after the new child so the closing tag will be indented properly. string parentWhitespace = XmlElement.PreviousSibling.Value; var indentedWhitespaceNode = XmlDocument.CreateWhitespace(parentWhitespace + DEFAULT_INDENT); XmlElement.InsertBefore(indentedWhitespaceNode, child.XmlElement); var unindentedWhitespaceNode = XmlDocument.CreateWhitespace(parentWhitespace); XmlElement.InsertAfter(unindentedWhitespaceNode, child.XmlElement); } } _count++; MarkDirty("Add child element named '{0}'", child.ElementName); }
/// <summary> /// Insert the child before the reference child. /// Reference child if provided must be parented by this element. /// Reference child may be null, in which case this is equivalent to <see cref="AppendChild">AppendChild(child)</see>. /// Throws if the parent is not itself parented. /// Throws if the reference node does not have this node as its parent. /// Throws if the node to add is already parented. /// Throws if the node to add was created from a different project than this node. /// </summary> /// <remarks> /// Semantics are those of XmlNode.InsertBeforeChild. /// </remarks> public void InsertBeforeChild(ProjectElement child, ProjectElement reference) { ErrorUtilities.VerifyThrowArgumentNull(child, "child"); if (reference == null) { AppendChild(child); return; } VerifyForInsertBeforeAfterFirst(child, reference); child.VerifyThrowInvalidOperationAcceptableLocation(this, reference.PreviousSibling, reference); child.Parent = this; if (FirstChild == reference) { FirstChild = child; } child.PreviousSibling = reference.PreviousSibling; child.NextSibling = reference; reference.PreviousSibling = child; if (child.PreviousSibling != null) { ErrorUtilities.VerifyThrow(child.PreviousSibling.NextSibling == reference, "Invalid structure"); child.PreviousSibling.NextSibling = child; } XmlElement.InsertBefore(child.XmlElement, reference.XmlElement); if (XmlDocument.PreserveWhitespace) { // If we are trying to preserve formatting of the file, then the new node won't automatically be indented. // So try to match the surrounding formatting by by checking the whitespace that precedes where we inserted // the new node, and inserting the same whitespace between the node we added and the one after it. if (child.XmlElement.PreviousSibling != null && child.XmlElement.PreviousSibling.NodeType == XmlNodeType.Whitespace) { var newWhitespaceNode = XmlDocument.CreateWhitespace(child.XmlElement.PreviousSibling.Value); XmlElement.InsertBefore(newWhitespaceNode, reference.XmlElement); } } _count++; MarkDirty("Insert element {0}", child.ElementName); }
/// <summary> /// Sets the first child in this container /// </summary> private void AddInitialChild(ProjectElement child) { ErrorUtilities.VerifyThrow(FirstChild == null && LastChild == null, "Expecting no children"); VerifyForInsertBeforeAfterFirst(child, null); child.VerifyThrowInvalidOperationAcceptableLocation(this, null, null); child.Parent = this; FirstChild = child; LastChild = child; child.PreviousSibling = null; child.NextSibling = null; XmlElement.AppendChild(child.XmlElement); _count++; MarkDirty("Add child element named '{0}'", child.ElementName); }
/// <summary> /// Insert the child before the reference child. /// Reference child if provided must be parented by this element. /// Reference child may be null, in which case this is equivalent to <see cref="AppendChild">AppendChild(child)</see>. /// Throws if the parent is not itself parented. /// Throws if the reference node does not have this node as its parent. /// Throws if the node to add is already parented. /// Throws if the node to add was created from a different project than this node. /// </summary> /// <remarks> /// Semantics are those of XmlNode.InsertBeforeChild. /// </remarks> public void InsertBeforeChild(ProjectElement child, ProjectElement reference) { ErrorUtilities.VerifyThrowArgumentNull(child, nameof(child)); if (reference == null) { AppendChild(child); return; } VerifyForInsertBeforeAfterFirst(child, reference); child.VerifyThrowInvalidOperationAcceptableLocation(this, reference.PreviousSibling, reference); child.Parent = this; if (FirstChild == reference) { FirstChild = child; } child.PreviousSibling = reference.PreviousSibling; child.NextSibling = reference; reference.PreviousSibling = child; if (child.PreviousSibling != null) { ErrorUtilities.VerifyThrow(child.PreviousSibling.NextSibling == reference, "Invalid structure"); child.PreviousSibling.NextSibling = child; } AddToXml(child); Count++; MarkDirty("Insert element {0}", child.ElementName); }
/// <summary> /// Insert the child after the reference child. /// Reference child if provided must be parented by this element. /// Reference child may be null, in which case this is equivalent to <see cref="PrependChild">PrependChild(child)</see>. /// Throws if the parent is not itself parented. /// Throws if the reference node does not have this node as its parent. /// Throws if the node to add is already parented. /// Throws if the node to add was created from a different project than this node. /// </summary> /// <remarks> /// Semantics are those of XmlNode.InsertAfterChild. /// </remarks> public void InsertAfterChild(ProjectElement child, ProjectElement reference) { ErrorUtilities.VerifyThrowArgumentNull(child, "child"); if (reference == null) { PrependChild(child); return; } VerifyForInsertBeforeAfterFirst(child, reference); child.VerifyThrowInvalidOperationAcceptableLocation(this, reference, reference.NextSibling); child.Parent = this; if (LastChild == reference) { LastChild = child; } child.PreviousSibling = reference; child.NextSibling = reference.NextSibling; reference.NextSibling = child; if (child.NextSibling != null) { ErrorUtilities.VerifyThrow(child.NextSibling.PreviousSibling == reference, "Invalid structure"); child.NextSibling.PreviousSibling = child; } XmlElement.InsertAfter(child.XmlElement, reference.XmlElement); _count++; MarkDirty("Insert element {0}", child.ElementName); }