// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
namespace ICSharpCode.NRefactory.CSharp
{
///
/// Represents the children of an AstNode that have a specific role.
///
public class AstNodeCollection : ICollection where T : AstNode
{
readonly AstNode node;
readonly Role role;
public AstNodeCollection(AstNode node, Role role)
{
if (node == null)
throw new ArgumentNullException("node");
if (role == null)
throw new ArgumentNullException("role");
this.node = node;
this.role = role;
}
public int Count {
get {
var e = GetEnumerator();
int count = 0;
while (e.MoveNext())
count++;
return count;
}
}
public void Add(T element)
{
node.AddChild(element, role);
}
public void AddRange(IEnumerable nodes)
{
// Evaluate 'nodes' first, since it might change when we add the new children
// Example: collection.AddRange(collection);
if (nodes != null) {
foreach (T node in nodes.ToList())
Add(node);
}
}
public void AddRange(T[] nodes)
{
// Fast overload for arrays - we don't need to create a copy
if (nodes != null) {
foreach (T node in nodes)
Add(node);
}
}
public void ReplaceWith(IEnumerable nodes)
{
// Evaluate 'nodes' first, since it might change when we call Clear()
// Example: collection.ReplaceWith(collection);
if (nodes != null)
nodes = nodes.ToList();
Clear();
foreach (T node in nodes)
Add(node);
}
public void MoveTo(ICollection targetCollection)
{
foreach (T node in this) {
node.Remove();
targetCollection.Add(node);
}
}
public bool Contains(T element)
{
return element != null && element.Parent == node && element.Role == role;
}
public bool Remove(T element)
{
if (Contains(element)) {
element.Remove();
return true;
} else {
return false;
}
}
public void CopyTo(T[] array, int arrayIndex)
{
foreach (T item in this)
array[arrayIndex++] = item;
}
public void Clear()
{
foreach (T item in this)
item.Remove();
}
bool ICollection.IsReadOnly {
get { return false; }
}
public IEnumerator GetEnumerator()
{
AstNode next;
for (AstNode cur = node.FirstChild; cur != null; cur = next) {
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.NextSibling;
if (cur.Role == role)
yield return (T)cur;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
if (obj is AstNodeCollection) {
return ((AstNodeCollection)obj) == this;
} else {
return false;
}
}
public override int GetHashCode()
{
return node.GetHashCode() ^ role.GetHashCode();
}
public static bool operator ==(AstNodeCollection left, AstNodeCollection right)
{
return left.role == right.role && left.node == right.node;
}
public static bool operator !=(AstNodeCollection left, AstNodeCollection right)
{
return !(left.role == right.role && left.node == right.node);
}
#endregion
internal bool DoMatch(AstNodeCollection other, Match match)
{
Stack patternStack = new Stack();
Stack stack = new Stack();
patternStack.Push(this.node.FirstChild);
stack.Push(new Pattern.PossibleMatch(other.node.FirstChild, match.CheckPoint()));
while (stack.Count > 0) {
AstNode cur1 = patternStack.Pop();
AstNode cur2 = stack.Peek().NextOther;
match.RestoreCheckPoint(stack.Pop().Checkpoint);
bool success = true;
while (cur1 != null && success) {
while (cur1 != null && cur1.Role != role)
cur1 = cur1.NextSibling;
while (cur2 != null && cur2.Role != role)
cur2 = cur2.NextSibling;
if (cur1 == null)
break;
Pattern pattern = cur1 as Pattern;
if (pattern == null && cur1.NodeType == NodeType.Placeholder)
pattern = cur1.GetChildByRole(TypePlaceholder.ChildRole) as Pattern;
if (pattern != null) {
Debug.Assert(stack.Count == patternStack.Count);
success = pattern.DoMatchCollection(role, cur2, match, stack);
Debug.Assert(stack.Count >= patternStack.Count);
while (stack.Count > patternStack.Count)
patternStack.Push(cur1.NextSibling);
} else {
success = cur1.DoMatch(cur2, match);
}
cur1 = cur1.NextSibling;
if (cur2 != null)
cur2 = cur2.NextSibling;
}
while (cur2 != null && cur2.Role != role)
cur2 = cur2.NextSibling;
if (success && cur2 == null)
return true;
}
return false;
}
}
}