// HtmlAgilityPack V1.0 - Simon Mourier
using System;
using System.Collections;
using System.Collections.Generic;
namespace HtmlAgilityPack
{
///
/// Represents a combined list and collection of HTML nodes.
///
public class HtmlNodeCollection : IList
{
#region Fields
private readonly HtmlNode _parentnode;
private readonly List _items = new List();
#endregion
#region Constructors
///
/// Initialize the HtmlNodeCollection with the base parent node
///
/// The base node of the collection
public HtmlNodeCollection(HtmlNode parentnode)
{
_parentnode = parentnode; // may be null
}
#endregion
#region Properties
///
/// Gets a given node from the list.
///
public int this[HtmlNode node]
{
get
{
int index = GetNodeIndex(node);
if (index == -1)
{
throw new ArgumentOutOfRangeException("node",
"Node \"" + node.CloneNode(false).OuterHtml +
"\" was not found in the collection");
}
return index;
}
}
///
/// Get node with tag name
///
///
///
public HtmlNode this[string nodeName]
{
get
{
nodeName = nodeName.ToLower();
for (int i = 0; i < _items.Count; i++)
if (_items[i].Equals(nodeName))
return _items[i];
return null;
}
}
#endregion
#region IList Members
///
/// Gets the number of elements actually contained in the list.
///
public int Count
{
get { return _items.Count; }
}
///
/// Is collection read only
///
public bool IsReadOnly
{
get { return false; }
}
///
/// Gets the node at the specified index.
///
public HtmlNode this[int index]
{
get { return _items[index]; }
set { _items[index] = value; }
}
///
/// Add node to the collection
///
///
public void Add(HtmlNode node)
{
_items.Add(node);
}
///
/// Clears out the collection of HtmlNodes. Removes each nodes reference to parentnode, nextnode and prevnode
///
public void Clear()
{
foreach (HtmlNode node in _items)
{
node.ParentNode = null;
node.NextSibling = null;
node.PreviousSibling = null;
}
_items.Clear();
}
///
/// Gets existence of node in collection
///
///
///
public bool Contains(HtmlNode item)
{
return _items.Contains(item);
}
///
/// Copy collection to array
///
///
///
public void CopyTo(HtmlNode[] array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}
///
/// Get Enumerator
///
///
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
///
/// Get Explicit Enumerator
///
///
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
///
/// Get index of node
///
///
///
public int IndexOf(HtmlNode item)
{
return _items.IndexOf(item);
}
///
/// Insert node at index
///
///
///
public void Insert(int index, HtmlNode node)
{
HtmlNode next = null;
HtmlNode prev = null;
if (index > 0)
{
prev = _items[index - 1];
}
if (index < _items.Count)
{
next = _items[index];
}
_items.Insert(index, node);
if (prev != null)
{
if (node == prev)
{
throw new InvalidProgramException("Unexpected error.");
}
prev._nextnode = node;
}
if (next != null)
{
next._prevnode = node;
}
node._prevnode = prev;
if (next == node)
{
throw new InvalidProgramException("Unexpected error.");
}
node._nextnode = next;
node._parentnode = _parentnode;
}
///
/// Remove node
///
///
///
public bool Remove(HtmlNode item)
{
int i = _items.IndexOf(item);
RemoveAt(i);
return true;
}
///
/// Remove at index
///
///
public void RemoveAt(int index)
{
HtmlNode next = null;
HtmlNode prev = null;
HtmlNode oldnode = _items[index];
if (index > 0)
{
prev = _items[index - 1];
}
if (index < (_items.Count - 1))
{
next = _items[index + 1];
}
_items.RemoveAt(index);
if (prev != null)
{
if (next == prev)
{
throw new InvalidProgramException("Unexpected error.");
}
prev._nextnode = next;
}
if (next != null)
{
next._prevnode = prev;
}
oldnode._prevnode = null;
oldnode._nextnode = null;
oldnode._parentnode = null;
}
#endregion
#region Public Methods
///
/// Get first instance of node in supplied collection
///
///
///
///
public static HtmlNode FindFirst(HtmlNodeCollection items, string name)
{
foreach (HtmlNode node in items)
{
if (node.Name.ToLower().Contains(name))
return node;
if (node.HasChildNodes)
{
HtmlNode returnNode = FindFirst(node.ChildNodes, name);
if (returnNode != null)
return returnNode;
}
}
return null;
}
///
/// Add node to the end of the collection
///
///
public void Append(HtmlNode node)
{
HtmlNode last = null;
if (_items.Count > 0)
{
last = _items[_items.Count - 1];
}
_items.Add(node);
node._prevnode = last;
node._nextnode = null;
node._parentnode = _parentnode;
if (last != null)
{
if (last == node)
{
throw new InvalidProgramException("Unexpected error.");
}
last._nextnode = node;
}
}
///
/// Get first instance of node with name
///
///
///
public HtmlNode FindFirst(string name)
{
return FindFirst(this, name);
}
///
/// Get index of node
///
///
///
public int GetNodeIndex(HtmlNode node)
{
// TODO: should we rewrite this? what would be the key of a node?
for (int i = 0; i < _items.Count; i++)
{
if (node == (_items[i]))
{
return i;
}
}
return -1;
}
///
/// Add node to the beginning of the collection
///
///
public void Prepend(HtmlNode node)
{
HtmlNode first = null;
if (_items.Count > 0)
{
first = _items[0];
}
_items.Insert(0, node);
if (node == first)
{
throw new InvalidProgramException("Unexpected error.");
}
node._nextnode = first;
node._prevnode = null;
node._parentnode = _parentnode;
if (first != null)
{
first._prevnode = node;
}
}
///
/// Remove node at index
///
///
///
public bool Remove(int index)
{
RemoveAt(index);
return true;
}
///
/// Replace node at index
///
///
///
public void Replace(int index, HtmlNode node)
{
HtmlNode next = null;
HtmlNode prev = null;
HtmlNode oldnode = _items[index];
if (index > 0)
{
prev = _items[index - 1];
}
if (index < (_items.Count - 1))
{
next = _items[index + 1];
}
_items[index] = node;
if (prev != null)
{
if (node == prev)
{
throw new InvalidProgramException("Unexpected error.");
}
prev._nextnode = node;
}
if (next != null)
{
next._prevnode = node;
}
node._prevnode = prev;
if (next == node)
{
throw new InvalidProgramException("Unexpected error.");
}
node._nextnode = next;
node._parentnode = _parentnode;
oldnode._prevnode = null;
oldnode._nextnode = null;
oldnode._parentnode = null;
}
#endregion
#region LINQ Methods
///
/// Get all node descended from this collection
///
///
public IEnumerable DescendantNodes()
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.DescendantNodes())
yield return n;
}
///
/// Get all node descended from this collection
///
///
public IEnumerable Descendants()
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.Descendants())
yield return n;
}
///
/// Get all node descended from this collection with matching name
///
///
public IEnumerable Descendants(string name)
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.Descendants(name))
yield return n;
}
///
/// Gets all first generation elements in collection
///
///
public IEnumerable Elements()
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.ChildNodes)
yield return n;
}
///
/// Gets all first generation elements matching name
///
///
///
public IEnumerable Elements(string name)
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.Elements(name))
yield return n;
}
///
/// All first generation nodes in collection
///
///
public IEnumerable Nodes()
{
foreach (HtmlNode item in _items)
foreach (HtmlNode n in item.ChildNodes)
yield return n;
}
#endregion
}
}