// 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.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)
{
AstNode cur1 = this.node.FirstChild;
AstNode cur2 = other.node.FirstChild;
while (true) {
while (cur1 != null && cur1.Role != role)
cur1 = cur1.NextSibling;
while (cur2 != null && cur2.Role != role)
cur2 = cur2.NextSibling;
if (cur1 == null || cur2 == null)
break;
if (!cur1.DoMatch(cur2, match))
return false;
cur1 = cur1.NextSibling;
cur2 = cur2.NextSibling;
}
return cur1 == null && cur2 == null;
}
}
}