~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to contrib/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
using System;
27
 
using System.Collections;
28
 
using System.Collections.Generic;
29
 
using System.Diagnostics;
30
 
using System.IO;
31
 
using System.Linq;
32
 
using System.Threading;
33
 
 
34
 
namespace ICSharpCode.NRefactory.CSharp
35
 
{
36
 
        public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode
37
 
        {
38
 
                // the Root role must be available when creating the null nodes, so we can't put it in the Roles class
39
 
                internal static readonly Role<AstNode> RootRole = new Role<AstNode> ("Root");
40
 
                
41
 
                #region Null
42
 
                public static readonly AstNode Null = new NullAstNode ();
43
 
                
44
 
                sealed class NullAstNode : AstNode
45
 
                {
46
 
                        public override NodeType NodeType {
47
 
                                get {
48
 
                                        return NodeType.Unknown;
49
 
                                }
50
 
                        }
51
 
                        
52
 
                        public override bool IsNull {
53
 
                                get {
54
 
                                        return true;
55
 
                                }
56
 
                        }
57
 
                        
58
 
                        public override void AcceptVisitor (IAstVisitor visitor)
59
 
                        {
60
 
                        }
61
 
                        
62
 
                        public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
63
 
                        {
64
 
                                return default (T);
65
 
                        }
66
 
                        
67
 
                        public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
68
 
                        {
69
 
                                return default (S);
70
 
                        }
71
 
                        
72
 
                        protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
73
 
                        {
74
 
                                return other == null || other.IsNull;
75
 
                        }
76
 
                }
77
 
                #endregion
78
 
                
79
 
                #region PatternPlaceholder
80
 
                public static implicit operator AstNode (PatternMatching.Pattern pattern)
81
 
                {
82
 
                        return pattern != null ? new PatternPlaceholder (pattern) : null;
83
 
                }
84
 
                
85
 
                sealed class PatternPlaceholder : AstNode, PatternMatching.INode
86
 
                {
87
 
                        readonly PatternMatching.Pattern child;
88
 
                        
89
 
                        public PatternPlaceholder (PatternMatching.Pattern child)
90
 
                        {
91
 
                                this.child = child;
92
 
                        }
93
 
                        
94
 
                        public override NodeType NodeType {
95
 
                                get { return NodeType.Pattern; }
96
 
                        }
97
 
                        
98
 
                        public override void AcceptVisitor (IAstVisitor visitor)
99
 
                        {
100
 
                                visitor.VisitPatternPlaceholder (this, child);
101
 
                        }
102
 
                        
103
 
                        public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
104
 
                        {
105
 
                                return visitor.VisitPatternPlaceholder (this, child);
106
 
                        }
107
 
 
108
 
                        public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
109
 
                        {
110
 
                                return visitor.VisitPatternPlaceholder (this, child, data);
111
 
                        }
112
 
                        
113
 
                        protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
114
 
                        {
115
 
                                return child.DoMatch (other, match);
116
 
                        }
117
 
                        
118
 
                        bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
119
 
                        {
120
 
                                return child.DoMatchCollection (role, pos, match, backtrackingInfo);
121
 
                        }
122
 
                }
123
 
                #endregion
124
 
                
125
 
                AstNode parent;
126
 
                AstNode prevSibling;
127
 
                AstNode nextSibling;
128
 
                AstNode firstChild;
129
 
                AstNode lastChild;
130
 
                
131
 
                // Flags, from least significant to most significant bits:
132
 
                // - Role.RoleIndexBits: role index
133
 
                // - 1 bit: IsFrozen
134
 
                protected uint flags = RootRole.Index;
135
 
                // Derived classes may also use a few bits,
136
 
                // for example Identifier uses 1 bit for IsVerbatim
137
 
                
138
 
                const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1;
139
 
                const uint frozenBit = 1u << Role.RoleIndexBits;
140
 
                protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1;
141
 
                
142
 
                protected AstNode()
143
 
                {
144
 
                        if (IsNull)
145
 
                                Freeze();
146
 
                }
147
 
                
148
 
                public bool IsFrozen {
149
 
                        get { return (flags & frozenBit) != 0; }
150
 
                }
151
 
                
152
 
                public void Freeze()
153
 
                {
154
 
                        if (!IsFrozen) {
155
 
                                for (AstNode child = firstChild; child != null; child = child.nextSibling)
156
 
                                        child.Freeze();
157
 
                                flags |= frozenBit;
158
 
                        }
159
 
                }
160
 
                
161
 
                protected void ThrowIfFrozen()
162
 
                {
163
 
                        if (IsFrozen)
164
 
                                throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name);
165
 
                }
166
 
                
167
 
                public abstract NodeType NodeType {
168
 
                        get;
169
 
                }
170
 
                
171
 
                public virtual bool IsNull {
172
 
                        get {
173
 
                                return false;
174
 
                        }
175
 
                }
176
 
                
177
 
                public virtual TextLocation StartLocation {
178
 
                        get {
179
 
                                var child = firstChild;
180
 
                                if (child == null)
181
 
                                        return TextLocation.Empty;
182
 
                                return child.StartLocation;
183
 
                        }
184
 
                }
185
 
                
186
 
                public virtual TextLocation EndLocation {
187
 
                        get {
188
 
                                var child = lastChild;
189
 
                                if (child == null)
190
 
                                        return TextLocation.Empty;
191
 
                                return child.EndLocation;
192
 
                        }
193
 
                }
194
 
                
195
 
                /// <summary>
196
 
                /// Gets the region from StartLocation to EndLocation for this node.
197
 
                /// The file name of the region is set based on the parent CompilationUnit's file name.
198
 
                /// If this node is not connected to a whole compilation, the file name will be null.
199
 
                /// </summary>
200
 
                public ICSharpCode.NRefactory.TypeSystem.DomRegion GetRegion()
201
 
                {
202
 
                        var cu = (this.Ancestors.LastOrDefault() ?? this) as CompilationUnit;
203
 
                        string fileName = (cu != null ? cu.FileName : null);
204
 
                        return new ICSharpCode.NRefactory.TypeSystem.DomRegion(fileName, this.StartLocation, this.EndLocation);
205
 
                }
206
 
                
207
 
                public AstNode Parent {
208
 
                        get { return parent; }
209
 
                }
210
 
                
211
 
                public Role Role {
212
 
                        get {
213
 
                                return Role.GetByIndex(flags & roleIndexMask);
214
 
                        }
215
 
                        set {
216
 
                                if (value == null)
217
 
                                        throw new ArgumentNullException("value");
218
 
                                if (!value.IsValid(this))
219
 
                                        throw new ArgumentException("This node is not valid in the new role.");
220
 
                                ThrowIfFrozen();
221
 
                                SetRole(value);
222
 
                        }
223
 
                }
224
 
                
225
 
                void SetRole(Role role)
226
 
                {
227
 
                        flags = (flags & ~roleIndexMask) | role.Index;
228
 
                }
229
 
                
230
 
                public AstNode NextSibling {
231
 
                        get { return nextSibling; }
232
 
                }
233
 
                
234
 
                public AstNode PrevSibling {
235
 
                        get { return prevSibling; }
236
 
                }
237
 
                
238
 
                public AstNode FirstChild {
239
 
                        get { return firstChild; }
240
 
                }
241
 
                
242
 
                public AstNode LastChild {
243
 
                        get { return lastChild; }
244
 
                }
245
 
                
246
 
                public bool HasChildren {
247
 
                        get {
248
 
                                return firstChild != null;
249
 
                        }
250
 
                }
251
 
                
252
 
                public IEnumerable<AstNode> Children {
253
 
                        get {
254
 
                                AstNode next;
255
 
                                for (AstNode cur = firstChild; cur != null; cur = next) {
256
 
                                        Debug.Assert (cur.parent == this);
257
 
                                        // Remember next before yielding cur.
258
 
                                        // This allows removing/replacing nodes while iterating through the list.
259
 
                                        next = cur.nextSibling;
260
 
                                        yield return cur;
261
 
                                }
262
 
                        }
263
 
                }
264
 
                
265
 
                /// <summary>
266
 
                /// Gets the ancestors of this node (excluding this node itself)
267
 
                /// </summary>
268
 
                public IEnumerable<AstNode> Ancestors {
269
 
                        get {
270
 
                                for (AstNode cur = parent; cur != null; cur = cur.parent) {
271
 
                                        yield return cur;
272
 
                                }
273
 
                        }
274
 
                }
275
 
                
276
 
                /// <summary>
277
 
                /// Gets the ancestors of this node (including this node itself)
278
 
                /// </summary>
279
 
                public IEnumerable<AstNode> AncestorsAndSelf {
280
 
                        get {
281
 
                                for (AstNode cur = this; cur != null; cur = cur.parent) {
282
 
                                        yield return cur;
283
 
                                }
284
 
                        }
285
 
                }
286
 
                
287
 
                /// <summary>
288
 
                /// Gets all descendants of this node (excluding this node itself).
289
 
                /// </summary>
290
 
                public IEnumerable<AstNode> Descendants {
291
 
                        get {
292
 
                                return Utils.TreeTraversal.PreOrder (this.Children, n => n.Children);
293
 
                        }
294
 
                }
295
 
                
296
 
                /// <summary>
297
 
                /// Gets all descendants of this node (including this node itself).
298
 
                /// </summary>
299
 
                public IEnumerable<AstNode> DescendantsAndSelf {
300
 
                        get {
301
 
                                return Utils.TreeTraversal.PreOrder (this, n => n.Children);
302
 
                        }
303
 
                }
304
 
                
305
 
                /// <summary>
306
 
                /// Gets the first child with the specified role.
307
 
                /// Returns the role's null object if the child is not found.
308
 
                /// </summary>
309
 
                public T GetChildByRole<T>(Role<T> role) where T : AstNode
310
 
                {
311
 
                        if (role == null)
312
 
                                throw new ArgumentNullException ("role");
313
 
                        uint roleIndex = role.Index;
314
 
                        for (var cur = firstChild; cur != null; cur = cur.nextSibling) {
315
 
                                if ((cur.flags & roleIndexMask) == roleIndex)
316
 
                                        return (T)cur;
317
 
                        }
318
 
                        return role.NullObject;
319
 
                }
320
 
                
321
 
                public T GetParent<T>() where T : AstNode
322
 
                {
323
 
                        return Ancestors.OfType<T>().FirstOrDefault();
324
 
                }
325
 
                                
326
 
                public AstNodeCollection<T> GetChildrenByRole<T> (Role<T> role) where T : AstNode
327
 
                {
328
 
                        return new AstNodeCollection<T> (this, role);
329
 
                }
330
 
                
331
 
                protected void SetChildByRole<T> (Role<T> role, T newChild) where T : AstNode
332
 
                {
333
 
                        AstNode oldChild = GetChildByRole (role);
334
 
                        if (oldChild.IsNull)
335
 
                                AddChild (newChild, role);
336
 
                        else
337
 
                                oldChild.ReplaceWith (newChild);
338
 
                }
339
 
                
340
 
                public void AddChild<T> (T child, Role<T> role) where T : AstNode
341
 
                {
342
 
                        if (role == null)
343
 
                                throw new ArgumentNullException ("role");
344
 
                        if (child == null || child.IsNull)
345
 
                                return;
346
 
                        ThrowIfFrozen();
347
 
                        if (child.parent != null)
348
 
                                throw new ArgumentException ("Node is already used in another tree.", "child");
349
 
                        if (child.IsFrozen)
350
 
                                throw new ArgumentException ("Cannot add a frozen node.", "child");
351
 
                        AddChildUnsafe (child, role);
352
 
                }
353
 
                
354
 
                /// <summary>
355
 
                /// Adds a child without performing any safety checks.
356
 
                /// </summary>
357
 
                void AddChildUnsafe (AstNode child, Role role)
358
 
                {
359
 
                        child.parent = this;
360
 
                        child.SetRole(role);
361
 
                        if (firstChild == null) {
362
 
                                lastChild = firstChild = child;
363
 
                        } else {
364
 
                                lastChild.nextSibling = child;
365
 
                                child.prevSibling = lastChild;
366
 
                                lastChild = child;
367
 
                        }
368
 
                }
369
 
 
370
 
                public void InsertChildsBefore<T>(AstNode nextSibling, Role<T> role, params T[] child) where T : AstNode
371
 
                {
372
 
                        foreach (var cur in child) {
373
 
                                InsertChildBefore(nextSibling, cur, role);
374
 
                        }
375
 
                }
376
 
                
377
 
                public void InsertChildBefore<T> (AstNode nextSibling, T child, Role<T> role) where T : AstNode
378
 
                {
379
 
                        if (role == null)
380
 
                                throw new ArgumentNullException ("role");
381
 
                        if (nextSibling == null || nextSibling.IsNull) {
382
 
                                AddChild (child, role);
383
 
                                return;
384
 
                        }
385
 
                        
386
 
                        if (child == null || child.IsNull)
387
 
                                return;
388
 
                        ThrowIfFrozen();
389
 
                        if (child.parent != null)
390
 
                                throw new ArgumentException ("Node is already used in another tree.", "child");
391
 
                        if (child.IsFrozen)
392
 
                                throw new ArgumentException ("Cannot add a frozen node.", "child");
393
 
                        if (nextSibling.parent != this)
394
 
                                throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling");
395
 
                        // No need to test for "Cannot add children to null nodes",
396
 
                        // as there isn't any valid nextSibling in null nodes.
397
 
                        InsertChildBeforeUnsafe (nextSibling, child, role);
398
 
                }
399
 
                
400
 
                void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role)
401
 
                {
402
 
                        child.parent = this;
403
 
                        child.SetRole(role);
404
 
                        child.nextSibling = nextSibling;
405
 
                        child.prevSibling = nextSibling.prevSibling;
406
 
                        
407
 
                        if (nextSibling.prevSibling != null) {
408
 
                                Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling);
409
 
                                nextSibling.prevSibling.nextSibling = child;
410
 
                        } else {
411
 
                                Debug.Assert (firstChild == nextSibling);
412
 
                                firstChild = child;
413
 
                        }
414
 
                        nextSibling.prevSibling = child;
415
 
                }
416
 
                
417
 
                public void InsertChildAfter<T> (AstNode prevSibling, T child, Role<T> role) where T : AstNode
418
 
                {
419
 
                        InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role);
420
 
                }
421
 
                
422
 
                /// <summary>
423
 
                /// Removes this node from its parent.
424
 
                /// </summary>
425
 
                public void Remove ()
426
 
                {
427
 
                        if (parent != null) {
428
 
                                ThrowIfFrozen();
429
 
                                if (prevSibling != null) {
430
 
                                        Debug.Assert (prevSibling.nextSibling == this);
431
 
                                        prevSibling.nextSibling = nextSibling;
432
 
                                } else {
433
 
                                        Debug.Assert (parent.firstChild == this);
434
 
                                        parent.firstChild = nextSibling;
435
 
                                }
436
 
                                if (nextSibling != null) {
437
 
                                        Debug.Assert (nextSibling.prevSibling == this);
438
 
                                        nextSibling.prevSibling = prevSibling;
439
 
                                } else {
440
 
                                        Debug.Assert (parent.lastChild == this);
441
 
                                        parent.lastChild = prevSibling;
442
 
                                }
443
 
                                parent = null;
444
 
                                prevSibling = null;
445
 
                                nextSibling = null;
446
 
                        }
447
 
                }
448
 
                
449
 
                /// <summary>
450
 
                /// Replaces this node with the new node.
451
 
                /// </summary>
452
 
                public void ReplaceWith (AstNode newNode)
453
 
                {
454
 
                        if (newNode == null || newNode.IsNull) {
455
 
                                Remove ();
456
 
                                return;
457
 
                        }
458
 
                        if (newNode == this)
459
 
                                return; // nothing to do...
460
 
                        if (parent == null) {
461
 
                                throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
462
 
                        }
463
 
                        ThrowIfFrozen();
464
 
                        // Because this method doesn't statically check the new node's type with the role,
465
 
                        // we perform a runtime test:
466
 
                        if (!this.Role.IsValid (newNode)) {
467
 
                                throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode");
468
 
                        }
469
 
                        if (newNode.parent != null) {
470
 
                                // newNode is used within this tree?
471
 
                                if (newNode.Ancestors.Contains (this)) {
472
 
                                        // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);"
473
 
                                        // enable automatic removal
474
 
                                        newNode.Remove ();
475
 
                                } else {
476
 
                                        throw new ArgumentException ("Node is already used in another tree.", "newNode");
477
 
                                }
478
 
                        }
479
 
                        if (newNode.IsFrozen)
480
 
                                throw new ArgumentException ("Cannot add a frozen node.", "newNode");
481
 
                        
482
 
                        newNode.parent = parent;
483
 
                        newNode.SetRole(this.Role);
484
 
                        newNode.prevSibling = prevSibling;
485
 
                        newNode.nextSibling = nextSibling;
486
 
 
487
 
                        if (prevSibling != null) {
488
 
                                Debug.Assert (prevSibling.nextSibling == this);
489
 
                                prevSibling.nextSibling = newNode;
490
 
                        } else {
491
 
                                Debug.Assert (parent.firstChild == this);
492
 
                                parent.firstChild = newNode;
493
 
                        }
494
 
                        if (nextSibling != null) {
495
 
                                Debug.Assert (nextSibling.prevSibling == this);
496
 
                                nextSibling.prevSibling = newNode;
497
 
                        } else {
498
 
                                Debug.Assert (parent.lastChild == this);
499
 
                                parent.lastChild = newNode;
500
 
                        }
501
 
                        parent = null;
502
 
                        prevSibling = null;
503
 
                        nextSibling = null;
504
 
                }
505
 
                
506
 
                public AstNode ReplaceWith (Func<AstNode, AstNode> replaceFunction)
507
 
                {
508
 
                        if (replaceFunction == null)
509
 
                                throw new ArgumentNullException ("replaceFunction");
510
 
                        if (parent == null) {
511
 
                                throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
512
 
                        }
513
 
                        AstNode oldParent = parent;
514
 
                        AstNode oldSuccessor = nextSibling;
515
 
                        Role oldRole = this.Role;
516
 
                        Remove ();
517
 
                        AstNode replacement = replaceFunction (this);
518
 
                        if (oldSuccessor != null && oldSuccessor.parent != oldParent)
519
 
                                throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?");
520
 
                        if (!(replacement == null || replacement.IsNull)) {
521
 
                                if (replacement.parent != null)
522
 
                                        throw new InvalidOperationException ("replace function must return the root of a tree");
523
 
                                if (!oldRole.IsValid (replacement)) {
524
 
                                        throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ()));
525
 
                                }
526
 
                                
527
 
                                if (oldSuccessor != null)
528
 
                                        oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole);
529
 
                                else
530
 
                                        oldParent.AddChildUnsafe (replacement, oldRole);
531
 
                        }
532
 
                        return replacement;
533
 
                }
534
 
                
535
 
                /// <summary>
536
 
                /// Clones the whole subtree starting at this AST node.
537
 
                /// </summary>
538
 
                /// <remarks>Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned.</remarks>
539
 
                public AstNode Clone ()
540
 
                {
541
 
                        AstNode copy = (AstNode)MemberwiseClone ();
542
 
                        // First, reset the shallow pointer copies
543
 
                        copy.parent = null;
544
 
                        copy.firstChild = null;
545
 
                        copy.lastChild = null;
546
 
                        copy.prevSibling = null;
547
 
                        copy.nextSibling = null;
548
 
                        copy.flags &= ~frozenBit; // unfreeze the copy
549
 
                        
550
 
                        // Then perform a deep copy:
551
 
                        for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) {
552
 
                                copy.AddChildUnsafe (cur.Clone (), cur.Role);
553
 
                        }
554
 
                        
555
 
                        // Finally, clone the annotation, if necessary
556
 
                        copy.CloneAnnotations();
557
 
                        
558
 
                        return copy;
559
 
                }
560
 
                
561
 
                public abstract void AcceptVisitor (IAstVisitor visitor);
562
 
                
563
 
                public abstract T AcceptVisitor<T> (IAstVisitor<T> visitor);
564
 
                
565
 
                public abstract S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data);
566
 
                
567
 
                #region Pattern Matching
568
 
                protected static bool MatchString (string pattern, string text)
569
 
                {
570
 
                        return PatternMatching.Pattern.MatchString(pattern, text);
571
 
                }
572
 
                
573
 
                protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match);
574
 
                
575
 
                bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match)
576
 
                {
577
 
                        AstNode o = other as AstNode;
578
 
                        // try matching if other is null, or if other is an AstNode
579
 
                        return (other == null || o != null) && DoMatch (o, match);
580
 
                }
581
 
                
582
 
                bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
583
 
                {
584
 
                        AstNode o = pos as AstNode;
585
 
                        return (pos == null || o != null) && DoMatch (o, match);
586
 
                }
587
 
                
588
 
                PatternMatching.INode PatternMatching.INode.NextSibling {
589
 
                        get { return nextSibling; }
590
 
                }
591
 
                
592
 
                PatternMatching.INode PatternMatching.INode.FirstChild {
593
 
                        get { return firstChild; }
594
 
                }
595
 
 
596
 
                #endregion
597
 
                
598
 
                public AstNode GetNextNode ()
599
 
                {
600
 
                        if (NextSibling != null)
601
 
                                return NextSibling;
602
 
                        if (Parent != null)
603
 
                                return Parent.GetNextNode ();
604
 
                        return null;
605
 
                }
606
 
 
607
 
                public AstNode GetPrevNode ()
608
 
                {
609
 
                        if (PrevSibling != null)
610
 
                                return PrevSibling;
611
 
                        if (Parent != null)
612
 
                                return Parent.GetPrevNode ();
613
 
                        return null;
614
 
                }
615
 
                // filters all non c# nodes (comments, white spaces or pre processor directives)
616
 
                public AstNode GetCSharpNodeBefore (AstNode node)
617
 
                {
618
 
                        var n = node.PrevSibling;
619
 
                        while (n != null) {
620
 
                                if (n.Role != Roles.Comment)
621
 
                                        return n;
622
 
                                n = n.GetPrevNode ();
623
 
                        }
624
 
                        return null;
625
 
                }
626
 
                
627
 
                #region GetNodeAt
628
 
                /// <summary>
629
 
                /// 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
630
 
                /// the current method declaration.
631
 
                /// (End exclusive)
632
 
                /// </summary>
633
 
                public AstNode GetNodeAt (int line, int column, Predicate<AstNode> pred = null)
634
 
                {
635
 
                        return GetNodeAt (new TextLocation (line, column), pred);
636
 
                }
637
 
                
638
 
                /// <summary>
639
 
                /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching
640
 
                /// the current method declaration.
641
 
                /// (End exclusive)
642
 
                /// </summary>
643
 
                public AstNode GetNodeAt (TextLocation location, Predicate<AstNode> pred = null)
644
 
                {
645
 
                        AstNode result = null;
646
 
                        AstNode node = this;
647
 
                        while (node.FirstChild != null) {
648
 
                                var child = node.FirstChild;
649
 
                                while (child != null) {
650
 
                                        if (child.StartLocation <= location && location < child.EndLocation) {
651
 
                                                if (pred == null || pred (child))
652
 
                                                        result = child;
653
 
                                                node = child;
654
 
                                                break;
655
 
                                        }
656
 
                                        child = child.NextSibling;
657
 
                                }
658
 
                                // found no better child node - therefore the parent is the right one.
659
 
                                if (child == null)
660
 
                                        break;
661
 
                        }
662
 
                        return result;
663
 
                }
664
 
                
665
 
                /// <summary>
666
 
                /// 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
667
 
                /// the current method declaration.
668
 
                /// (End exclusive)
669
 
                /// </summary>
670
 
                public T GetNodeAt<T> (int line, int column) where T : AstNode
671
 
                {
672
 
                        return GetNodeAt<T> (new TextLocation (line, column));
673
 
                }
674
 
                
675
 
                /// <summary>
676
 
                /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching
677
 
                /// the current method declaration.
678
 
                /// (End exclusive)
679
 
                /// </summary>
680
 
                public T GetNodeAt<T> (TextLocation location) where T : AstNode
681
 
                {
682
 
                        T result = null;
683
 
                        AstNode node = this;
684
 
                        while (node.FirstChild != null) {
685
 
                                var child = node.FirstChild;
686
 
                                while (child != null) {
687
 
                                        if (child.StartLocation <= location && location < child.EndLocation) {
688
 
                                                if (child is T)
689
 
                                                        result = (T)child;
690
 
                                                node = child;
691
 
                                                break;
692
 
                                        }
693
 
                                        child = child.NextSibling;
694
 
                                }
695
 
                                // found no better child node - therefore the parent is the right one.
696
 
                                if (child == null)
697
 
                                        break;
698
 
                        }
699
 
                        return result;
700
 
                }
701
 
 
702
 
                #endregion
703
 
 
704
 
                #region GetAdjacentNodeAt
705
 
                /// <summary>
706
 
                /// 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
707
 
                /// the current method declaration.
708
 
                /// (End inclusive)
709
 
                /// </summary>
710
 
                public AstNode GetAdjacentNodeAt(int line, int column, Predicate<AstNode> pred = null)
711
 
                {
712
 
                        return GetAdjacentNodeAt (new TextLocation (line, column), pred);
713
 
                }
714
 
                
715
 
                /// <summary>
716
 
                /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching
717
 
                /// the current method declaration.
718
 
                /// (End inclusive)
719
 
                /// </summary>
720
 
                public AstNode GetAdjacentNodeAt (TextLocation location, Predicate<AstNode> pred = null)
721
 
                {
722
 
                        AstNode result = null;
723
 
                        AstNode node = this;
724
 
                        while (node.FirstChild != null) {
725
 
                                var child = node.FirstChild;
726
 
                                while (child != null) {
727
 
                                        if (child.StartLocation <= location && location <= child.EndLocation) {
728
 
                                                if (pred == null || pred (child))
729
 
                                                        result = child;
730
 
                                                node = child;
731
 
                                                break;
732
 
                                        }
733
 
                                        child = child.NextSibling;
734
 
                                }
735
 
                                // found no better child node - therefore the parent is the right one.
736
 
                                if (child == null)
737
 
                                        break;
738
 
                        }
739
 
                        return result;
740
 
                }
741
 
                
742
 
                /// <summary>
743
 
                /// 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
744
 
                /// the current method declaration.
745
 
                /// (End inclusive)
746
 
                /// </summary>
747
 
                public T GetAdjacentNodeAt<T>(int line, int column) where T : AstNode
748
 
                {
749
 
                        return GetAdjacentNodeAt<T> (new TextLocation (line, column));
750
 
                }
751
 
                
752
 
                /// <summary>
753
 
                /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching
754
 
                /// the current method declaration.
755
 
                /// (End inclusive)
756
 
                /// </summary>
757
 
                public T GetAdjacentNodeAt<T> (TextLocation location) where T : AstNode
758
 
                {
759
 
                        T result = null;
760
 
                        AstNode node = this;
761
 
                        while (node.FirstChild != null) {
762
 
                                var child = node.FirstChild;
763
 
                                while (child != null) {
764
 
                                        if (child.StartLocation <= location && location < child.EndLocation) {
765
 
                                                if (child is T)
766
 
                                                        result = (T)child;
767
 
                                                node = child;
768
 
                                                break;
769
 
                                        }
770
 
                                        child = child.NextSibling;
771
 
                                }
772
 
                                // found no better child node - therefore the parent is the right one.
773
 
                                if (child == null)
774
 
                                        break;
775
 
                        }
776
 
                        return result;
777
 
                }
778
 
                #endregion
779
 
 
780
 
 
781
 
                /// <summary>
782
 
                /// Gets the node that fully contains the range from startLocation to endLocation.
783
 
                /// </summary>
784
 
                public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation)
785
 
                {
786
 
                        for (AstNode child = firstChild; child != null; child = child.nextSibling) {
787
 
                                if (child.StartLocation <= startLocation && endLocation <= child.EndLocation)
788
 
                                        return child.GetNodeContaining(startLocation, endLocation);
789
 
                        }
790
 
                        return this;
791
 
                }
792
 
                
793
 
                public IEnumerable<AstNode> GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn)
794
 
                {
795
 
                        return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn));
796
 
                }
797
 
                
798
 
                public IEnumerable<AstNode> GetNodesBetween (TextLocation start, TextLocation end)
799
 
                {
800
 
                        AstNode node = this;
801
 
                        while (node != null) {
802
 
                                AstNode next;
803
 
                                if (start <= node.StartLocation && node.EndLocation <= end) {
804
 
                                        // Remember next before yielding node.
805
 
                                        // This allows iteration to continue when the caller removes/replaces the node.
806
 
                                        next = node.NextSibling;
807
 
                                        yield return node;
808
 
                                } else {
809
 
                                        if (node.EndLocation <= start) {
810
 
                                                next = node.NextSibling;
811
 
                                        } else {
812
 
                                                next = node.FirstChild;
813
 
                                        }
814
 
                                }
815
 
                                
816
 
                                if (next != null && next.StartLocation > end)
817
 
                                        yield break;
818
 
                                node = next;
819
 
                        }
820
 
                }
821
 
                
822
 
                /// <summary>
823
 
                /// Gets the node as formatted C# output.
824
 
                /// </summary>
825
 
                /// <param name='formattingOptions'>
826
 
                /// Formatting options.
827
 
                /// </param>
828
 
                public virtual string GetText (CSharpFormattingOptions formattingOptions = null)
829
 
                {
830
 
                        if (IsNull)
831
 
                                return "";
832
 
                        var w = new StringWriter ();
833
 
                        AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ()));
834
 
                        return w.ToString ();
835
 
                }
836
 
                
837
 
                /// <summary>
838
 
                /// Returns true, if the given coordinates (line, column) are in the node.
839
 
                /// </summary>
840
 
                /// <returns>
841
 
                /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false.
842
 
                /// </returns>
843
 
                public bool Contains (int line, int column)
844
 
                {
845
 
                        return Contains (new TextLocation (line, column));
846
 
                }
847
 
                
848
 
                /// <summary>
849
 
                /// Returns true, if the given coordinates are in the node.
850
 
                /// </summary>
851
 
                /// <returns>
852
 
                /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false.
853
 
                /// </returns>
854
 
                public bool Contains (TextLocation location)
855
 
                {
856
 
                        return this.StartLocation <= location && location < this.EndLocation;
857
 
                }
858
 
                
859
 
                /// <summary>
860
 
                /// Returns true, if the given coordinates (line, column) are in the node.
861
 
                /// </summary>
862
 
                /// <returns>
863
 
                /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false.
864
 
                /// </returns>
865
 
                public bool IsInside (int line, int column)
866
 
                {
867
 
                        return IsInside (new TextLocation (line, column));
868
 
                }
869
 
                
870
 
                /// <summary>
871
 
                /// Returns true, if the given coordinates are in the node.
872
 
                /// </summary>
873
 
                /// <returns>
874
 
                /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false.
875
 
                /// </returns>
876
 
                public bool IsInside (TextLocation location)
877
 
                {
878
 
                        return this.StartLocation <= location && location <= this.EndLocation;
879
 
                }
880
 
                
881
 
                public override void AddAnnotation (object annotation)
882
 
                {
883
 
                        if (this.IsNull)
884
 
                                throw new InvalidOperationException ("Cannot add annotations to the null node");
885
 
                        base.AddAnnotation (annotation);
886
 
                }
887
 
                
888
 
                internal string DebugToString()
889
 
                {
890
 
                        if (IsNull)
891
 
                                return "Null";
892
 
                        string text = GetText();
893
 
                        text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " ");
894
 
                        if (text.Length > 100)
895
 
                                return text.Substring(0, 97) + "...";
896
 
                        else
897
 
                                return text;
898
 
                }
899
 
        }
900
 
}