diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 232d907c0a..2b6f1915b5 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -129,6 +129,26 @@ namespace ICSharpCode.NRefactory.CSharp } } + /// + /// Gets the ancestors of this node (excluding this node itself) + /// + public IEnumerable Ancestors { + get { + for (AstNode cur = parent; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets all descendants of this node (excluding this node itself). + /// + public IEnumerable Descendants { + get { + return Utils.TreeTraversal.PreOrder(this.Children, n => n.Children); + } + } + /// /// Gets the first child with the specified role. /// Returns the role's null object if the child is not found. @@ -291,20 +311,27 @@ namespace ICSharpCode.NRefactory.CSharp Remove(); return; } - if (this.parent == null) { + if (newNode == this) + return; // nothing to do... + if (parent == null) { throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } - if (newNode.parent != null) { - // TODO: what if newNode is used within *this* tree? - // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" - // We'll probably want to allow that. - throw new ArgumentException ("Node is already used in another tree.", "newNode"); - } // Because this method doesn't statically check the new node's type with the role, // we perform a runtime test: if (!role.IsValid(newNode)) { throw new ArgumentException (string.Format("The new node '{0}' is not valid in the role {1}", newNode.GetType().Name, role.ToString()), "newNode"); } + if (newNode.parent != null) { + // newNode is used within this tree? + if (newNode.Ancestors.Contains(this)) { + // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" + // enable automatic removal + newNode.Remove(); + } else { + throw new ArgumentException ("Node is already used in another tree.", "newNode"); + } + } + newNode.parent = parent; newNode.role = role; newNode.prevSibling = prevSibling; @@ -335,6 +362,9 @@ namespace ICSharpCode.NRefactory.CSharp { if (replaceFunction == null) throw new ArgumentNullException("replaceFunction"); + if (parent == null) { + throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } AstNode oldParent = parent; AstNode oldSuccessor = nextSibling; Role oldRole = role;