From ebcfe909bd251cb99f356f4fb5531650c8bc0102 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 4 Dec 2010 20:19:37 +0100 Subject: [PATCH] DomNode: enforce tree invariants, fix bug in InsertChildBefore, and add Remove/Replace methods. --- ICSharpCode.NRefactory/CSharp/Dom/DomNode.cs | 175 ++++++++++++------ .../CSharp/Parser/CSharpParser.cs | 14 +- 2 files changed, 129 insertions(+), 60 deletions(-) diff --git a/ICSharpCode.NRefactory/CSharp/Dom/DomNode.cs b/ICSharpCode.NRefactory/CSharp/Dom/DomNode.cs index 1a2239b9af..0d3ad02607 100644 --- a/ICSharpCode.NRefactory/CSharp/Dom/DomNode.cs +++ b/ICSharpCode.NRefactory/CSharp/Dom/DomNode.cs @@ -1,6 +1,6 @@ -// +// // AstNode.cs -// +// // Author: // Mike Krüger // @@ -26,13 +26,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp { public abstract class DomNode { + #region Null public static readonly DomNode Null = new NullAstNode (); - class NullAstNode : DomNode + sealed class NullAstNode : DomNode { public override NodeType NodeType { get { @@ -51,6 +53,14 @@ namespace ICSharpCode.NRefactory.CSharp return default (S); } } + #endregion + + DomNode parent; + DomNode prevSibling; + DomNode nextSibling; + DomNode firstChild; + DomNode lastChild; + int role; public abstract NodeType NodeType { get; @@ -63,8 +73,8 @@ namespace ICSharpCode.NRefactory.CSharp } public virtual DomLocation StartLocation { - get { - var child = FirstChild; + get { + var child = firstChild; if (child == null) return DomLocation.Empty; return child.StartLocation; @@ -72,8 +82,8 @@ namespace ICSharpCode.NRefactory.CSharp } public virtual DomLocation EndLocation { - get { - var child = LastChild; + get { + var child = lastChild; if (child == null) return DomLocation.Empty; return child.EndLocation; @@ -81,107 +91,166 @@ namespace ICSharpCode.NRefactory.CSharp } public DomNode Parent { - get; - set; + get { return parent; } } public int Role { - get; - set; + get { return role; } } public DomNode NextSibling { - get; - set; + get { return nextSibling; } } public DomNode PrevSibling { - get; - set; + get { return prevSibling; } } public DomNode FirstChild { - get; - set; + get { return firstChild; } } public DomNode LastChild { - get; - set; + get { return lastChild; } } public IEnumerable Children { get { - var cur = FirstChild; + var cur = firstChild; while (cur != null) { yield return cur; - cur = cur.NextSibling; + cur = cur.nextSibling; } } } public DomNode GetChildByRole (int role) { - var cur = FirstChild; + var cur = firstChild; while (cur != null) { - if (cur.Role == role) + if (cur.role == role) return cur; - cur = cur.NextSibling; + cur = cur.nextSibling; } return null; } public IEnumerable GetChildrenByRole (int role) { - var cur = FirstChild; + var cur = firstChild; while (cur != null) { - if (cur.Role == role) + if (cur.role == role) yield return cur; - cur = cur.NextSibling; + cur = cur.nextSibling; } } - public void AddChild (DomNode child) + public void AddChild (DomNode child, int role) { if (child == null) return; - child.Parent = this; - if (FirstChild == null) { - LastChild = FirstChild = child; + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + child.parent = this; + child.role = role; + if (firstChild == null) { + lastChild = firstChild = child; } else { - LastChild.NextSibling = child; - child.PrevSibling = LastChild; - LastChild = child; + lastChild.nextSibling = child; + child.prevSibling = lastChild; + lastChild = child; } } - public void AddChild (DomNode child, int role) + public void InsertChildBefore (DomNode nextSibling, DomNode child, int role) { + if (nextSibling == null) { + AddChild (child, role); + return; + } + if (child == null) return; - child.Role = role; - AddChild (child); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (nextSibling.parent != this) + throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); + + child.parent = this; + child.role = role; + child.nextSibling = nextSibling; + child.prevSibling = nextSibling.prevSibling; + + if (nextSibling.prevSibling != null) { + Debug.Assert(nextSibling.prevSibling.nextSibling == nextSibling); + nextSibling.prevSibling.nextSibling = child; + } else { + Debug.Assert(firstChild == nextSibling); + firstChild = child; + } + nextSibling.prevSibling = child; } - public void InsertChildBefore (DomNode nextSibling, DomNode child, int role) + /// + /// Removes this node from its parent. + /// + public void Remove() { - if (child == null) - return; - - if (FirstChild == null || nextSibling == null) { - AddChild (child, role); + if (parent != null) { + if (prevSibling != null) { + Debug.Assert(prevSibling.nextSibling == this); + prevSibling.nextSibling = nextSibling; + } else { + Debug.Assert(parent.firstChild == this); + parent.firstChild = nextSibling; + } + if (nextSibling != null) { + Debug.Assert(nextSibling.prevSibling == this); + nextSibling.prevSibling = prevSibling; + } else { + Debug.Assert(parent.lastChild == this); + parent.lastChild = prevSibling; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + } + + /// + /// Replaces this node with the new node. + /// + public void Replace(DomNode newNode) + { + if (newNode == null) { + Remove(); return; } - child.Parent = this; - child.Role = role; - - child.NextSibling = nextSibling; - - if (nextSibling.PrevSibling != null) { - child.PrevSibling = nextSibling.PrevSibling; - nextSibling.PrevSibling.NextSibling = child; + if (newNode.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "newNode"); + newNode.parent = parent; + newNode.role = role; + newNode.prevSibling = prevSibling; + newNode.nextSibling = nextSibling; + if (parent != null) { + if (prevSibling != null) { + Debug.Assert(prevSibling.nextSibling == this); + prevSibling.nextSibling = newNode; + } else { + Debug.Assert(parent.firstChild == this); + parent.firstChild = newNode; + } + if (nextSibling != null) { + Debug.Assert(nextSibling.prevSibling == this); + nextSibling.prevSibling = newNode; + } else { + Debug.Assert(parent.lastChild == this); + parent.lastChild = newNode; + } + parent = null; + prevSibling = null; + nextSibling = null; } - nextSibling.PrevSibling = child; } public abstract S AcceptVisitor (DomVisitor visitor, T data); @@ -205,7 +274,7 @@ namespace ICSharpCode.NRefactory.CSharp public const int TargetExpression = 14; public const int Member = 15; - // some pre defined constants for most used punctuation + // some pre defined constants for most used punctuation public const int LPar = 50; // ( public const int RPar = 51; // ) diff --git a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs index bcda63889d..049e6f496d 100644 --- a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs @@ -260,7 +260,7 @@ namespace ICSharpCode.NRefactory.CSharp if (namespaceStack.Count > 0) { namespaceStack.Peek ().AddChild (child, NamespaceDeclaration.Roles.Member); } else { - unit.AddChild (child); + unit.AddChild (child, CompilationUnit.Roles.Member); } } @@ -2224,7 +2224,7 @@ namespace ICSharpCode.NRefactory.CSharp NewAnonymousType aType = l.Expr as NewAnonymousType; AnonymousTypeParameter param = ((AnonymousTypeParameter)aType.Parameters[1]); - result.AddChild (new Identifier (param.Name, Convert (param.Location))); + result.AddChild (new Identifier (param.Name, Convert (param.Location)), Identifier.Roles.Identifier); if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[1]), 1), QueryExpressionWhereClause.Roles.Assign); @@ -2250,7 +2250,7 @@ namespace ICSharpCode.NRefactory.CSharp if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[0]), "join".Length), QueryExpressionJoinClause.JoinKeywordRole); - result.AddChild (new Identifier (join.JoinVariable.Name, Convert (join.JoinVariable.Location))); + result.AddChild (new Identifier (join.JoinVariable.Name, Convert (join.JoinVariable.Location)), Identifier.Roles.Identifier); if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[1]), "in".Length), QueryExpressionJoinClause.InKeywordRole); @@ -2275,7 +2275,7 @@ namespace ICSharpCode.NRefactory.CSharp if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[0]), "join".Length), QueryExpressionJoinClause.JoinKeywordRole); - result.AddChild (new Identifier (groupJoin.JoinVariable.Name, Convert (groupJoin.JoinVariable.Location))); + result.AddChild (new Identifier (groupJoin.JoinVariable.Name, Convert (groupJoin.JoinVariable.Location)), Identifier.Roles.Identifier); if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[1]), "in".Length), QueryExpressionJoinClause.InKeywordRole); @@ -2293,7 +2293,7 @@ namespace ICSharpCode.NRefactory.CSharp if (location != null) result.AddChild (new CSharpTokenNode (Convert (location[4]), "into".Length), QueryExpressionJoinClause.IntoKeywordRole); - result.AddChild (new Identifier (groupJoin.JoinVariable.Name, Convert (groupJoin.JoinVariable.Location))); + result.AddChild (new Identifier (groupJoin.JoinVariable.Name, Convert (groupJoin.JoinVariable.Location)), Identifier.Roles.Identifier); return result; } @@ -2352,7 +2352,7 @@ namespace ICSharpCode.NRefactory.CSharp void InsertComment (DomNode node, Comment comment) { if (node.EndLocation < comment.StartLocation) { - node.AddChild (comment); + node.AddChild (comment, DomNode.Roles.Comment); return; } @@ -2367,7 +2367,7 @@ namespace ICSharpCode.NRefactory.CSharp } } - node.AddChild (comment); + node.AddChild (comment, DomNode.Roles.Comment); } void InsertComments (CompilerCompilationUnit top, ConversionVisitor conversionVisitor)