protected virtual void OnMerge(MergeableNode <T> existingNode, MergeableNode <T> node) { // Process children. if (node.Children != null && node.Children.Count > 0) { EnsureChildren(existingNode); Merge(existingNode.Children, node.Children); } }
private static void EnsureChildren(MergeableNode <T> node) { Debug.Assert(node != null); if (node.Children == null) { node.Children = new MergeableNodeCollection <T>(); } }
public MergeableNode <T> GetRoot() { MergeableNode <T> node = this; while (node.Parent != null) { node = node.Parent; } return(node); }
protected virtual void OnInsert(MergeableNodeCollection <T> existingNodes, MergeableNode <T> node, int index) { if (!CloneNodesOnMerge) { // Moving nodes - destroying the input collection: node.Parent?.Children?.Remove(node); // First detach node from parent. existingNodes.Insert(index, node); } else { // Copying nodes - leaving the input collection intact. var copy = new MergeableNode <T>(node.Content); existingNodes.Insert(index, copy); // Process children if (node.Children != null && node.Children.Count > 0) { EnsureChildren(copy); Merge(copy.Children, node.Children); } } }
//-------------------------------------------------------------- #region Traversal Methods //-------------------------------------------------------------- /// <summary> /// Gets the children of the specified node. /// </summary> /// <param name="node">The node.</param> /// <returns>The children of <paramref name="node"/>.</returns> private static IEnumerable <MergeableNode <T> > GetChildren(MergeableNode <T> node) { return(node.Children ?? Enumerable.Empty <MergeableNode <T> >()); }
protected override void SetParent(MergeableNode <T> child, MergeableNode <T> parent) { child.Parent = parent; }
protected override MergeableNode <T> GetParent(MergeableNode <T> child) { return(child.Parent); }
private void Merge(MergeableNodeCollection <T> targetNodes, MergeableNode <T> node) { if (node == null) { return; } if (node.Content == null) { throw new NotSupportedException("Cannot merge nodes. MergeableNode must not be empty (Content != null)."); } if (node.MergePoints == null) { return; } foreach (var mergePoint in node.MergePoints) { string targetName = mergePoint.Target; var mergeOperation = mergePoint.Operation; if (mergeOperation == MergeOperation.Ignore) { return; // Ignore this node. } // Check whether node with the same action name already exists. int indexOfExistingNode = FindTargetNode(targetNodes, node.Content.Name); bool nodeExists = (indexOfExistingNode >= 0); int indexOfTarget = -1; bool targetFound = false; // Find target node. // (MergeOperation.Append and MergeOperation.Prepend do not need a target node.) if (!nodeExists && mergeOperation != MergeOperation.Append && mergeOperation != MergeOperation.Prepend) { indexOfTarget = FindTargetNode(targetNodes, targetName); targetFound = (indexOfTarget >= 0); if (!targetFound) { continue; // Target not found. Try next merge point. } } switch (mergeOperation) { case MergeOperation.Ignore: // Do nothing. Debug.Fail("We should never get here, cause all MergeOperation.Ignore are handled above."); break; case MergeOperation.Match: // Do not add this node, but merge children Debug.Assert(nodeExists || targetFound, "Sanity check."); if (nodeExists) { if (node.Children != null && node.Children.Count > 0) { var targetNode = targetNodes[indexOfExistingNode]; EnsureChildren(targetNode); Merge(targetNode.Children, node.Children); } } else { if (node.Children != null && node.Children.Count > 0) { var targetNode = targetNodes[indexOfTarget]; EnsureChildren(targetNode); Merge(targetNode.Children, node.Children); } } break; case MergeOperation.Prepend: // Merge node with existing node, or prepend at the beginning. if (nodeExists) { OnMerge(targetNodes[indexOfExistingNode], node); } else { OnInsert(targetNodes, node, 0); } break; case MergeOperation.Append: // Merge node with existing node, or append at the end. if (nodeExists) { OnMerge(targetNodes[indexOfExistingNode], node); } else { OnInsert(targetNodes, node, targetNodes.Count); } break; case MergeOperation.InsertBefore: // Merge node with existing, or insert before merge point. Debug.Assert(nodeExists || targetFound, "Sanity check."); if (nodeExists) { OnMerge(targetNodes[indexOfExistingNode], node); } else { OnInsert(targetNodes, node, indexOfTarget); } break; case MergeOperation.InsertAfter: // Merge node with existing or insert after merge point. Debug.Assert(nodeExists || targetFound, "Sanity check."); if (nodeExists) { OnMerge(targetNodes[indexOfExistingNode], node); } else { OnInsert(targetNodes, node, indexOfTarget + 1); } break; case MergeOperation.Replace: // Replace existing node (or node at merge point). Debug.Assert(nodeExists || targetFound, "Sanity check."); if (nodeExists) { targetNodes.RemoveAt(indexOfExistingNode); OnInsert(targetNodes, node, indexOfExistingNode); } else { targetNodes.RemoveAt(indexOfTarget); OnInsert(targetNodes, node, indexOfTarget); } break; case MergeOperation.Remove: // Remove existing node (or node at merge point) Debug.Assert(nodeExists || targetFound, "Sanity check."); if (nodeExists) { targetNodes.RemoveAt(indexOfExistingNode); } else { targetNodes.RemoveAt(indexOfTarget); } break; } } }