Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Ast/AstNode.cs @ 17197

Last change on this file since 17197 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 30.5 KB
Line 
1//
2// AstNode.cs
3//
4// Author:
5//       Mike Krüger <mkrueger@novell.com>
6//
7// Copyright (c) 2009 Novell, Inc (http://www.novell.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.IO;
32using System.Linq;
33using System.Threading;
34using ICSharpCode.NRefactory.TypeSystem;
35
36namespace ICSharpCode.NRefactory.CSharp
37{
38  public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode, ICloneable
39  {
40    // the Root role must be available when creating the null nodes, so we can't put it in the Roles class
41    internal static readonly Role<AstNode> RootRole = new Role<AstNode> ("Root");
42   
43    #region Null
44    public static readonly AstNode Null = new NullAstNode ();
45   
46    sealed class NullAstNode : AstNode
47    {
48      public override NodeType NodeType {
49        get {
50          return NodeType.Unknown;
51        }
52      }
53     
54      public override bool IsNull {
55        get {
56          return true;
57        }
58      }
59     
60      public override void AcceptVisitor (IAstVisitor visitor)
61      {
62        visitor.VisitNullNode(this);
63      }
64     
65      public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
66      {
67        return visitor.VisitNullNode(this);
68      }
69     
70      public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
71      {
72        return visitor.VisitNullNode(this, data);
73      }
74     
75      protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
76      {
77        return other == null || other.IsNull;
78      }
79    }
80    #endregion
81   
82    #region PatternPlaceholder
83    public static implicit operator AstNode (PatternMatching.Pattern pattern)
84    {
85      return pattern != null ? new PatternPlaceholder (pattern) : null;
86    }
87   
88    sealed class PatternPlaceholder : AstNode, PatternMatching.INode
89    {
90      readonly PatternMatching.Pattern child;
91     
92      public PatternPlaceholder (PatternMatching.Pattern child)
93      {
94        this.child = child;
95      }
96     
97      public override NodeType NodeType {
98        get { return NodeType.Pattern; }
99      }
100     
101      public override void AcceptVisitor (IAstVisitor visitor)
102      {
103        visitor.VisitPatternPlaceholder (this, child);
104      }
105     
106      public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
107      {
108        return visitor.VisitPatternPlaceholder (this, child);
109      }
110
111      public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
112      {
113        return visitor.VisitPatternPlaceholder (this, child, data);
114      }
115     
116      protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
117      {
118        return child.DoMatch (other, match);
119      }
120     
121      bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
122      {
123        return child.DoMatchCollection (role, pos, match, backtrackingInfo);
124      }
125    }
126    #endregion
127   
128    AstNode parent;
129    AstNode prevSibling;
130    AstNode nextSibling;
131    AstNode firstChild;
132    AstNode lastChild;
133   
134    // Flags, from least significant to most significant bits:
135    // - Role.RoleIndexBits: role index
136    // - 1 bit: IsFrozen
137    protected uint flags = RootRole.Index;
138    // Derived classes may also use a few bits,
139    // for example Identifier uses 1 bit for IsVerbatim
140   
141    const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1;
142    const uint frozenBit = 1u << Role.RoleIndexBits;
143    protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1;
144   
145    protected AstNode()
146    {
147      if (IsNull)
148        Freeze();
149    }
150   
151    public bool IsFrozen {
152      get { return (flags & frozenBit) != 0; }
153    }
154   
155    public void Freeze()
156    {
157      if (!IsFrozen) {
158        for (AstNode child = firstChild; child != null; child = child.nextSibling)
159          child.Freeze();
160        flags |= frozenBit;
161      }
162    }
163   
164    protected void ThrowIfFrozen()
165    {
166      if (IsFrozen)
167        throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name);
168    }
169   
170    public abstract NodeType NodeType {
171      get;
172    }
173   
174    public virtual bool IsNull {
175      get {
176        return false;
177      }
178    }
179   
180    public virtual TextLocation StartLocation {
181      get {
182        var child = firstChild;
183        if (child == null)
184          return TextLocation.Empty;
185        return child.StartLocation;
186      }
187    }
188   
189    public virtual TextLocation EndLocation {
190      get {
191        var child = lastChild;
192        if (child == null)
193          return TextLocation.Empty;
194        return child.EndLocation;
195      }
196    }
197
198    public DomRegion Region {
199      get {
200        return new DomRegion (StartLocation, EndLocation);
201      }
202    }
203   
204    /// <summary>
205    /// Gets the region from StartLocation to EndLocation for this node.
206    /// The file name of the region is set based on the parent SyntaxTree's file name.
207    /// If this node is not connected to a whole compilation, the file name will be null.
208    /// </summary>
209    public ICSharpCode.NRefactory.TypeSystem.DomRegion GetRegion()
210    {
211      var syntaxTree = (this.Ancestors.LastOrDefault() ?? this) as SyntaxTree;
212      string fileName = (syntaxTree != null ? syntaxTree.FileName : null);
213      return new ICSharpCode.NRefactory.TypeSystem.DomRegion(fileName, this.StartLocation, this.EndLocation);
214    }
215   
216    public AstNode Parent {
217      get { return parent; }
218    }
219   
220    public Role Role {
221      get {
222        return Role.GetByIndex(flags & roleIndexMask);
223      }
224      set {
225        if (value == null)
226          throw new ArgumentNullException("value");
227        if (!value.IsValid(this))
228          throw new ArgumentException("This node is not valid in the new role.");
229        ThrowIfFrozen();
230        SetRole(value);
231      }
232    }
233   
234    internal uint RoleIndex {
235      get { return flags & roleIndexMask; }
236    }
237   
238    void SetRole(Role role)
239    {
240      flags = (flags & ~roleIndexMask) | role.Index;
241    }
242   
243    public AstNode NextSibling {
244      get { return nextSibling; }
245    }
246   
247    public AstNode PrevSibling {
248      get { return prevSibling; }
249    }
250   
251    public AstNode FirstChild {
252      get { return firstChild; }
253    }
254   
255    public AstNode LastChild {
256      get { return lastChild; }
257    }
258   
259    public bool HasChildren {
260      get {
261        return firstChild != null;
262      }
263    }
264   
265    public IEnumerable<AstNode> Children {
266      get {
267        AstNode next;
268        for (AstNode cur = firstChild; cur != null; cur = next) {
269          Debug.Assert (cur.parent == this);
270          // Remember next before yielding cur.
271          // This allows removing/replacing nodes while iterating through the list.
272          next = cur.nextSibling;
273          yield return cur;
274        }
275      }
276    }
277   
278    /// <summary>
279    /// Gets the ancestors of this node (excluding this node itself)
280    /// </summary>
281    public IEnumerable<AstNode> Ancestors {
282      get {
283        for (AstNode cur = parent; cur != null; cur = cur.parent) {
284          yield return cur;
285        }
286      }
287    }
288   
289    /// <summary>
290    /// Gets the ancestors of this node (including this node itself)
291    /// </summary>
292    public IEnumerable<AstNode> AncestorsAndSelf {
293      get {
294        for (AstNode cur = this; cur != null; cur = cur.parent) {
295          yield return cur;
296        }
297      }
298    }
299   
300    /// <summary>
301    /// Gets all descendants of this node (excluding this node itself) in pre-order.
302    /// </summary>
303    public IEnumerable<AstNode> Descendants {
304      get { return GetDescendantsImpl(false); }
305    }
306   
307    /// <summary>
308    /// Gets all descendants of this node (including this node itself) in pre-order.
309    /// </summary>
310    public IEnumerable<AstNode> DescendantsAndSelf {
311      get { return GetDescendantsImpl(true); }
312    }
313
314    static bool IsInsideRegion(DomRegion region, AstNode pos)
315    {
316      if (region.IsEmpty)
317        return true;
318      var nodeRegion = pos.Region;
319      return region.IntersectsWith(nodeRegion) || region.OverlapsWith(nodeRegion);
320    }
321
322    public IEnumerable<AstNode> DescendantNodes (Func<AstNode, bool> descendIntoChildren = null)
323    {
324      return GetDescendantsImpl(false, new DomRegion (), descendIntoChildren);
325    }
326
327    public IEnumerable<AstNode> DescendantNodes (DomRegion region, Func<AstNode, bool> descendIntoChildren = null)
328    {
329      return GetDescendantsImpl(false, region, descendIntoChildren);
330    }
331
332    public IEnumerable<AstNode> DescendantNodesAndSelf (Func<AstNode, bool> descendIntoChildren = null)
333    {
334      return GetDescendantsImpl(true, new DomRegion (), descendIntoChildren);
335    }
336
337    public IEnumerable<AstNode> DescendantNodesAndSelf (DomRegion region, Func<AstNode, bool> descendIntoChildren = null)
338    {
339      return GetDescendantsImpl(true, region, descendIntoChildren);
340    }
341
342    IEnumerable<AstNode> GetDescendantsImpl(bool includeSelf, DomRegion region = new DomRegion (), Func<AstNode, bool> descendIntoChildren = null)
343    {
344      if (includeSelf) {
345        if (IsInsideRegion (region, this))
346          yield return this;
347        if (descendIntoChildren != null && !descendIntoChildren(this))
348          yield break;
349      }
350
351      Stack<AstNode> nextStack = new Stack<AstNode>();
352      nextStack.Push(null);
353      AstNode pos = firstChild;
354      while (pos != null) {
355        // Remember next before yielding pos.
356        // This allows removing/replacing nodes while iterating through the list.
357        if (pos.nextSibling != null)
358          nextStack.Push(pos.nextSibling);
359        if (IsInsideRegion(region, pos))
360          yield return pos;
361        if (pos.firstChild != null && (descendIntoChildren == null || descendIntoChildren(pos)))
362          pos = pos.firstChild;
363        else
364          pos = nextStack.Pop();
365      }
366    }
367   
368    /// <summary>
369    /// Gets the first child with the specified role.
370    /// Returns the role's null object if the child is not found.
371    /// </summary>
372    public T GetChildByRole<T>(Role<T> role) where T : AstNode
373    {
374      if (role == null)
375        throw new ArgumentNullException ("role");
376      uint roleIndex = role.Index;
377      for (var cur = firstChild; cur != null; cur = cur.nextSibling) {
378        if ((cur.flags & roleIndexMask) == roleIndex)
379          return (T)cur;
380      }
381      return role.NullObject;
382    }
383   
384    public T GetParent<T>() where T : AstNode
385    {
386      return Ancestors.OfType<T>().FirstOrDefault();
387    }
388
389    public AstNode GetParent(Func<AstNode, bool> pred)
390    {
391      return Ancestors.FirstOrDefault(pred);
392    }
393
394    public AstNodeCollection<T> GetChildrenByRole<T> (Role<T> role) where T : AstNode
395    {
396      return new AstNodeCollection<T> (this, role);
397    }
398   
399    protected void SetChildByRole<T> (Role<T> role, T newChild) where T : AstNode
400    {
401      AstNode oldChild = GetChildByRole (role);
402      if (oldChild.IsNull)
403        AddChild (newChild, role);
404      else
405        oldChild.ReplaceWith (newChild);
406    }
407   
408    public void AddChild<T> (T child, Role<T> role) where T : AstNode
409    {
410      if (role == null)
411        throw new ArgumentNullException ("role");
412      if (child == null || child.IsNull)
413        return;
414      ThrowIfFrozen();
415      if (child == this)
416        throw new ArgumentException ("Cannot add a node to itself as a child.", "child");
417      if (child.parent != null)
418        throw new ArgumentException ("Node is already used in another tree.", "child");
419      if (child.IsFrozen)
420        throw new ArgumentException ("Cannot add a frozen node.", "child");
421      AddChildUnsafe (child, role);
422    }
423   
424    public void AddChildWithExistingRole (AstNode child)
425    {
426      if (child == null || child.IsNull)
427        return;
428      ThrowIfFrozen();
429      if (child == this)
430        throw new ArgumentException ("Cannot add a node to itself as a child.", "child");
431      if (child.parent != null)
432        throw new ArgumentException ("Node is already used in another tree.", "child");
433      if (child.IsFrozen)
434        throw new ArgumentException ("Cannot add a frozen node.", "child");
435      AddChildUnsafe (child, child.Role);
436    }
437   
438    /// <summary>
439    /// Adds a child without performing any safety checks.
440    /// </summary>
441    internal void AddChildUnsafe (AstNode child, Role role)
442    {
443      child.parent = this;
444      child.SetRole(role);
445      if (firstChild == null) {
446        lastChild = firstChild = child;
447      } else {
448        lastChild.nextSibling = child;
449        child.prevSibling = lastChild;
450        lastChild = child;
451      }
452    }
453
454    public void InsertChildBefore<T> (AstNode nextSibling, T child, Role<T> role) where T : AstNode
455    {
456      if (role == null)
457        throw new ArgumentNullException ("role");
458      if (nextSibling == null || nextSibling.IsNull) {
459        AddChild (child, role);
460        return;
461      }
462     
463      if (child == null || child.IsNull)
464        return;
465      ThrowIfFrozen();
466      if (child.parent != null)
467        throw new ArgumentException ("Node is already used in another tree.", "child");
468      if (child.IsFrozen)
469        throw new ArgumentException ("Cannot add a frozen node.", "child");
470      if (nextSibling.parent != this)
471        throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling");
472      // No need to test for "Cannot add children to null nodes",
473      // as there isn't any valid nextSibling in null nodes.
474      InsertChildBeforeUnsafe (nextSibling, child, role);
475    }
476   
477    internal void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role)
478    {
479      child.parent = this;
480      child.SetRole(role);
481      child.nextSibling = nextSibling;
482      child.prevSibling = nextSibling.prevSibling;
483     
484      if (nextSibling.prevSibling != null) {
485        Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling);
486        nextSibling.prevSibling.nextSibling = child;
487      } else {
488        Debug.Assert (firstChild == nextSibling);
489        firstChild = child;
490      }
491      nextSibling.prevSibling = child;
492    }
493   
494    public void InsertChildAfter<T> (AstNode prevSibling, T child, Role<T> role) where T : AstNode
495    {
496      InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role);
497    }
498   
499    /// <summary>
500    /// Removes this node from its parent.
501    /// </summary>
502    public void Remove ()
503    {
504      if (parent != null) {
505        ThrowIfFrozen();
506        if (prevSibling != null) {
507          Debug.Assert (prevSibling.nextSibling == this);
508          prevSibling.nextSibling = nextSibling;
509        } else {
510          Debug.Assert (parent.firstChild == this);
511          parent.firstChild = nextSibling;
512        }
513        if (nextSibling != null) {
514          Debug.Assert (nextSibling.prevSibling == this);
515          nextSibling.prevSibling = prevSibling;
516        } else {
517          Debug.Assert (parent.lastChild == this);
518          parent.lastChild = prevSibling;
519        }
520        parent = null;
521        prevSibling = null;
522        nextSibling = null;
523      }
524    }
525   
526    /// <summary>
527    /// Replaces this node with the new node.
528    /// </summary>
529    public void ReplaceWith (AstNode newNode)
530    {
531      if (newNode == null || newNode.IsNull) {
532        Remove ();
533        return;
534      }
535      if (newNode == this)
536        return; // nothing to do...
537      if (parent == null) {
538        throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
539      }
540      ThrowIfFrozen();
541      // Because this method doesn't statically check the new node's type with the role,
542      // we perform a runtime test:
543      if (!this.Role.IsValid (newNode)) {
544        throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode");
545      }
546      if (newNode.parent != null) {
547        // newNode is used within this tree?
548        if (newNode.Ancestors.Contains (this)) {
549          // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);"
550          // enable automatic removal
551          newNode.Remove ();
552        } else {
553          throw new ArgumentException ("Node is already used in another tree.", "newNode");
554        }
555      }
556      if (newNode.IsFrozen)
557        throw new ArgumentException ("Cannot add a frozen node.", "newNode");
558     
559      newNode.parent = parent;
560      newNode.SetRole(this.Role);
561      newNode.prevSibling = prevSibling;
562      newNode.nextSibling = nextSibling;
563
564      if (prevSibling != null) {
565        Debug.Assert (prevSibling.nextSibling == this);
566        prevSibling.nextSibling = newNode;
567      } else {
568        Debug.Assert (parent.firstChild == this);
569        parent.firstChild = newNode;
570      }
571      if (nextSibling != null) {
572        Debug.Assert (nextSibling.prevSibling == this);
573        nextSibling.prevSibling = newNode;
574      } else {
575        Debug.Assert (parent.lastChild == this);
576        parent.lastChild = newNode;
577      }
578      parent = null;
579      prevSibling = null;
580      nextSibling = null;
581    }
582   
583    public AstNode ReplaceWith (Func<AstNode, AstNode> replaceFunction)
584    {
585      if (replaceFunction == null)
586        throw new ArgumentNullException ("replaceFunction");
587      if (parent == null) {
588        throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
589      }
590      AstNode oldParent = parent;
591      AstNode oldSuccessor = nextSibling;
592      Role oldRole = this.Role;
593      Remove ();
594      AstNode replacement = replaceFunction (this);
595      if (oldSuccessor != null && oldSuccessor.parent != oldParent)
596        throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?");
597      if (!(replacement == null || replacement.IsNull)) {
598        if (replacement.parent != null)
599          throw new InvalidOperationException ("replace function must return the root of a tree");
600        if (!oldRole.IsValid (replacement)) {
601          throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ()));
602        }
603       
604        if (oldSuccessor != null)
605          oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole);
606        else
607          oldParent.AddChildUnsafe (replacement, oldRole);
608      }
609      return replacement;
610    }
611   
612    /// <summary>
613    /// Clones the whole subtree starting at this AST node.
614    /// </summary>
615    /// <remarks>Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned.</remarks>
616    public AstNode Clone ()
617    {
618      AstNode copy = (AstNode)MemberwiseClone ();
619      // First, reset the shallow pointer copies
620      copy.parent = null;
621      copy.firstChild = null;
622      copy.lastChild = null;
623      copy.prevSibling = null;
624      copy.nextSibling = null;
625      copy.flags &= ~frozenBit; // unfreeze the copy
626     
627      // Then perform a deep copy:
628      for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) {
629        copy.AddChildUnsafe (cur.Clone (), cur.Role);
630      }
631     
632      // Finally, clone the annotation, if necessary
633      copy.CloneAnnotations();
634     
635      return copy;
636    }
637   
638    object ICloneable.Clone()
639    {
640      return Clone();
641    }
642   
643    public abstract void AcceptVisitor (IAstVisitor visitor);
644   
645    public abstract T AcceptVisitor<T> (IAstVisitor<T> visitor);
646   
647    public abstract S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data);
648   
649    #region Pattern Matching
650    protected static bool MatchString (string pattern, string text)
651    {
652      return PatternMatching.Pattern.MatchString(pattern, text);
653    }
654   
655    protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match);
656   
657    bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match)
658    {
659      AstNode o = other as AstNode;
660      // try matching if other is null, or if other is an AstNode
661      return (other == null || o != null) && DoMatch (o, match);
662    }
663   
664    bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
665    {
666      AstNode o = pos as AstNode;
667      return (pos == null || o != null) && DoMatch (o, match);
668    }
669   
670    PatternMatching.INode PatternMatching.INode.NextSibling {
671      get { return nextSibling; }
672    }
673   
674    PatternMatching.INode PatternMatching.INode.FirstChild {
675      get { return firstChild; }
676    }
677
678    #endregion
679   
680    public AstNode GetNextNode ()
681    {
682      if (NextSibling != null)
683        return NextSibling;
684      if (Parent != null)
685        return Parent.GetNextNode ();
686      return null;
687    }
688
689    /// <summary>
690    /// Gets the next node which fullfills a given predicate
691    /// </summary>
692    /// <returns>The next node.</returns>
693    /// <param name="pred">The predicate.</param>
694    public AstNode GetNextNode (Func<AstNode, bool> pred)
695    {
696      var next = GetNextNode();
697      while (next != null && !pred (next))
698        next = next.GetNextNode();
699      return next;
700    }
701
702    public AstNode GetPrevNode ()
703    {
704      if (PrevSibling != null)
705        return PrevSibling;
706      if (Parent != null)
707        return Parent.GetPrevNode ();
708      return null;
709    }
710
711    /// <summary>
712    /// Gets the previous node which fullfills a given predicate
713    /// </summary>
714    /// <returns>The next node.</returns>
715    /// <param name="pred">The predicate.</param>
716    public AstNode GetPrevNode (Func<AstNode, bool> pred)
717    {
718      var prev = GetPrevNode();
719      while (prev != null && !pred (prev))
720        prev = prev.GetPrevNode();
721      return prev;
722    }
723    // filters all non c# nodes (comments, white spaces or pre processor directives)
724    public AstNode GetCSharpNodeBefore (AstNode node)
725    {
726      var n = node.PrevSibling;
727      while (n != null) {
728        if (n.Role != Roles.Comment)
729          return n;
730        n = n.GetPrevNode ();
731      }
732      return null;
733    }
734
735    /// <summary>
736    /// Gets the next sibling which fullfills a given predicate
737    /// </summary>
738    /// <returns>The next node.</returns>
739    /// <param name="pred">The predicate.</param>
740    public AstNode GetNextSibling (Func<AstNode, bool> pred)
741    {
742      var next = NextSibling;
743      while (next != null && !pred (next))
744        next = next.NextSibling;
745      return next;
746    }
747
748    /// <summary>
749    /// Gets the next sibling which fullfills a given predicate
750    /// </summary>
751    /// <returns>The next node.</returns>
752    /// <param name="pred">The predicate.</param>
753    public AstNode GetPrevSibling (Func<AstNode, bool> pred)
754    {
755      var prev = PrevSibling;
756      while (prev != null && !pred (prev))
757        prev = prev.PrevSibling;
758      return prev;
759    }
760   
761    #region GetNodeAt
762    /// <summary>
763    /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching
764    /// the current method declaration.
765    /// (End exclusive)
766    /// </summary>
767    public AstNode GetNodeAt (int line, int column, Predicate<AstNode> pred = null)
768    {
769      return GetNodeAt (new TextLocation (line, column), pred);
770    }
771   
772    /// <summary>
773    /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching
774    /// the current method declaration.
775    /// (End exclusive)
776    /// </summary>
777    public AstNode GetNodeAt (TextLocation location, Predicate<AstNode> pred = null)
778    {
779      AstNode result = null;
780      AstNode node = this;
781      while (node.LastChild != null) {
782        var child = node.LastChild;
783        while (child != null && child.StartLocation > location)
784          child = child.prevSibling;
785        if (child != null && location < child.EndLocation) {
786          if (pred == null || pred (child))
787            result = child;
788          node = child;
789        } else {
790          // found no better child node - therefore the parent is the right one.
791          break;
792        }
793      }
794      return result;
795    }
796   
797    /// <summary>
798    /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching
799    /// the current method declaration.
800    /// (End exclusive)
801    /// </summary>
802    public T GetNodeAt<T> (int line, int column) where T : AstNode
803    {
804      return GetNodeAt<T> (new TextLocation (line, column));
805    }
806   
807    /// <summary>
808    /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching
809    /// the current method declaration.
810    /// (End exclusive)
811    /// </summary>
812    public T GetNodeAt<T> (TextLocation location) where T : AstNode
813    {
814      T result = null;
815      AstNode node = this;
816      while (node.LastChild != null) {
817        var child = node.LastChild;
818        while (child != null && child.StartLocation > location)
819          child = child.prevSibling;
820        if (child != null && location < child.EndLocation) {
821          if (child is T)
822            result = (T)child;
823          node = child;
824        } else {
825          // found no better child node - therefore the parent is the right one.
826          break;
827        }
828      }
829      return result;
830    }
831
832    #endregion
833
834    #region GetAdjacentNodeAt
835    /// <summary>
836    /// Gets the node specified by pred at the location line, column. This is useful for getting a specific node from the tree. For example searching
837    /// the current method declaration.
838    /// (End inclusive)
839    /// </summary>
840    public AstNode GetAdjacentNodeAt(int line, int column, Predicate<AstNode> pred = null)
841    {
842      return GetAdjacentNodeAt (new TextLocation (line, column), pred);
843    }
844   
845    /// <summary>
846    /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching
847    /// the current method declaration.
848    /// (End inclusive)
849    /// </summary>
850    public AstNode GetAdjacentNodeAt (TextLocation location, Predicate<AstNode> pred = null)
851    {
852      AstNode result = null;
853      AstNode node = this;
854      while (node.LastChild != null) {
855        var child = node.LastChild;
856        while (child != null && child.StartLocation > location)
857          child = child.prevSibling;
858        if (child != null && location <= child.EndLocation) {
859          if (pred == null || pred (child))
860            result = child;
861          node = child;
862        } else {
863          // found no better child node - therefore the parent is the right one.
864          break;
865        }
866      }
867      return result;
868    }
869   
870    /// <summary>
871    /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching
872    /// the current method declaration.
873    /// (End inclusive)
874    /// </summary>
875    public T GetAdjacentNodeAt<T>(int line, int column) where T : AstNode
876    {
877      return GetAdjacentNodeAt<T> (new TextLocation (line, column));
878    }
879   
880    /// <summary>
881    /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching
882    /// the current method declaration.
883    /// (End inclusive)
884    /// </summary>
885    public T GetAdjacentNodeAt<T> (TextLocation location) where T : AstNode
886    {
887      T result = null;
888      AstNode node = this;
889      while (node.LastChild != null) {
890        var child = node.LastChild;
891        while (child != null && child.StartLocation > location)
892          child = child.prevSibling;
893        if (child != null && location <= child.EndLocation) {
894          if (child is T)
895            result = (T)child;
896          node = child;
897        } else {
898          // found no better child node - therefore the parent is the right one.
899          break;
900        }
901      }
902      return result;
903    }
904    #endregion
905
906
907    /// <summary>
908    /// Gets the node that fully contains the range from startLocation to endLocation.
909    /// </summary>
910    public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation)
911    {
912      for (AstNode child = firstChild; child != null; child = child.nextSibling) {
913        if (child.StartLocation <= startLocation && endLocation <= child.EndLocation)
914          return child.GetNodeContaining(startLocation, endLocation);
915      }
916      return this;
917    }
918   
919    /// <summary>
920    /// Returns the root nodes of all subtrees that are fully contained in the specified region.
921    /// </summary>
922    public IEnumerable<AstNode> GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn)
923    {
924      return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn));
925    }
926   
927    /// <summary>
928    /// Returns the root nodes of all subtrees that are fully contained between <paramref name="start"/> and <paramref name="end"/> (inclusive).
929    /// </summary>
930    public IEnumerable<AstNode> GetNodesBetween (TextLocation start, TextLocation end)
931    {
932      AstNode node = this;
933      while (node != null) {
934        AstNode next;
935        if (start <= node.StartLocation && node.EndLocation <= end) {
936          // Remember next before yielding node.
937          // This allows iteration to continue when the caller removes/replaces the node.
938          next = node.GetNextNode();
939          yield return node;
940        } else {
941          if (node.EndLocation <= start) {
942            next = node.GetNextNode();
943          } else {
944            next = node.FirstChild;
945          }
946        }
947       
948        if (next != null && next.StartLocation > end)
949          yield break;
950        node = next;
951      }
952    }
953    [Obsolete("Use ToString(options).")]
954    public string GetText (CSharpFormattingOptions formattingOptions = null)
955    {
956      return ToString(formattingOptions);
957    }
958
959    /// <summary>
960    /// Gets the node as formatted C# output.
961    /// </summary>
962    /// <param name='formattingOptions'>
963    /// Formatting options.
964    /// </param>
965    public virtual string ToString (CSharpFormattingOptions formattingOptions)
966    {
967      if (IsNull)
968        return "";
969      var w = new StringWriter ();
970      AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ()));
971      return w.ToString ();
972    }
973
974    public sealed override string ToString()
975    {
976      return ToString(null);
977    }
978
979    /// <summary>
980    /// Returns true, if the given coordinates (line, column) are in the node.
981    /// </summary>
982    /// <returns>
983    /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false.
984    /// </returns>
985    public bool Contains (int line, int column)
986    {
987      return Contains (new TextLocation (line, column));
988    }
989   
990    /// <summary>
991    /// Returns true, if the given coordinates are in the node.
992    /// </summary>
993    /// <returns>
994    /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false.
995    /// </returns>
996    public bool Contains (TextLocation location)
997    {
998      return this.StartLocation <= location && location < this.EndLocation;
999    }
1000   
1001    /// <summary>
1002    /// Returns true, if the given coordinates (line, column) are in the node.
1003    /// </summary>
1004    /// <returns>
1005    /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false.
1006    /// </returns>
1007    public bool IsInside (int line, int column)
1008    {
1009      return IsInside (new TextLocation (line, column));
1010    }
1011   
1012    /// <summary>
1013    /// Returns true, if the given coordinates are in the node.
1014    /// </summary>
1015    /// <returns>
1016    /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false.
1017    /// </returns>
1018    public bool IsInside (TextLocation location)
1019    {
1020      return this.StartLocation <= location && location <= this.EndLocation;
1021    }
1022   
1023    public override void AddAnnotation (object annotation)
1024    {
1025      if (this.IsNull)
1026        throw new InvalidOperationException ("Cannot add annotations to the null node");
1027      base.AddAnnotation (annotation);
1028    }
1029   
1030    internal string DebugToString()
1031    {
1032      if (IsNull)
1033        return "Null";
1034      string text = ToString();
1035      text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " ");
1036      if (text.Length > 100)
1037        return text.Substring(0, 97) + "...";
1038      else
1039        return text;
1040    }
1041  }
1042}
Note: See TracBrowser for help on using the repository browser.