// 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 } }