Node
Represents a node in the generated tree.
using AngleSharp.Dom.Collections;
using AngleSharp.Extensions;
using AngleSharp.Html;
using AngleSharp.Linq;
using System;
using System.Diagnostics;
using System.Text;
namespace AngleSharp.Dom
{
[DebuggerStepThrough]
internal class Node : EventTarget, INode, IEventTarget, IEquatable<INode>
{
private readonly NodeType _type;
private readonly string _name;
private readonly NodeFlags _flags;
private Document _owner;
private Url _baseUri;
private Node _parent;
private NodeList _children;
private TextRange _range;
public TextRange OriginalPosition {
get {
return _range;
}
internal set {
_range = value;
}
}
public bool HasChildNodes => _children.Length != 0;
public string BaseUri {
get {
Url baseUrl = BaseUrl;
if (baseUrl != null)
return baseUrl.Href;
return string.Empty;
}
}
public Url BaseUrl {
get {
if (_baseUri != null)
return _baseUri;
if (_parent != null)
return _parent.BaseUrl;
if (_owner != null)
return _owner._baseUri ?? _owner.DocumentUrl;
if (_type == NodeType.Document)
return ((Document)this).DocumentUrl;
return null;
}
set {
_baseUri = value;
}
}
public NodeType NodeType => _type;
public virtual string NodeValue {
get {
return null;
}
set {
}
}
public virtual string TextContent {
get {
return null;
}
set {
}
}
INode INode.PreviousSibling {
get {
return PreviousSibling;
}
}
INode INode.NextSibling {
get {
return NextSibling;
}
}
INode INode.FirstChild {
get {
return FirstChild;
}
}
INode INode.LastChild {
get {
return LastChild;
}
}
IDocument INode.Owner {
get {
return Owner;
}
}
INode INode.Parent {
get {
return _parent;
}
}
public IElement ParentElement => _parent as IElement;
INodeList INode.ChildNodes {
get {
return _children;
}
}
public string NodeName => _name;
internal Node PreviousSibling {
get {
if (_parent == null)
return null;
int length = _parent._children.Length;
for (int i = 1; i < length; i++) {
if (_parent._children[i] == this)
return _parent._children[i - 1];
}
return null;
}
}
internal Node NextSibling {
get {
if (_parent == null)
return null;
int num = _parent._children.Length - 1;
for (int i = 0; i < num; i++) {
if (_parent._children[i] == this)
return _parent._children[i + 1];
}
return null;
}
}
internal Node FirstChild {
get {
if (_children.Length <= 0)
return null;
return _children[0];
}
}
internal Node LastChild {
get {
if (_children.Length <= 0)
return null;
return _children[_children.Length - 1];
}
}
internal NodeFlags Flags => _flags;
internal NodeList ChildNodes {
get {
return _children;
}
set {
_children = value;
}
}
internal Node Parent {
get {
return _parent;
}
set {
_parent = value;
}
}
internal Document Owner {
get {
if (_type == NodeType.Document)
return null;
return _owner;
}
set {
if (_owner != value) {
Document owner = _owner;
_owner = value;
for (int i = 0; i < _children.Length; i++) {
_children[i].Owner = value;
}
if (owner != null)
NodeIsAdopted(owner);
}
}
}
internal Node(Document owner, string name, NodeType type = NodeType.Element, NodeFlags flags = NodeFlags.None)
{
_owner = owner;
_name = (name ?? string.Empty);
_type = type;
_children = new NodeList();
_flags = flags;
}
internal void AppendText(string s)
{
TextNode textNode = LastChild as TextNode;
if (textNode == null)
AddNode(new TextNode(Owner, s));
else
textNode.Append(s);
}
internal void InsertText(int index, string s)
{
if (index > 0 && index <= _children.Length && _children[index - 1].NodeType == NodeType.Text)
((IText)_children[index - 1]).Append(s);
else if (index >= 0 && index < _children.Length && _children[index].NodeType == NodeType.Text) {
((IText)_children[index]).Insert(0, s);
} else {
InsertNode(index, new TextNode(Owner, s));
}
}
public INode AppendChild(INode child)
{
return this.PreInsert(child, null);
}
public INode InsertChild(int index, INode child)
{
return this.PreInsert(child, _children[index]);
}
public INode InsertBefore(INode newElement, INode referenceElement)
{
return this.PreInsert(newElement, referenceElement);
}
public INode ReplaceChild(INode newChild, INode oldChild)
{
return ReplaceChild(newChild as Node, oldChild as Node, false);
}
public INode RemoveChild(INode child)
{
return this.PreRemove(child);
}
public virtual INode Clone(bool deep = true)
{
Node node = new Node(_owner, _name, _type, _flags);
CopyProperties(this, node, deep);
return node;
}
public DocumentPositions CompareDocumentPosition(INode otherNode)
{
if (this == otherNode)
return DocumentPositions.Same;
if (_owner != otherNode.Owner)
return (DocumentPositions)(33 | ((otherNode.GetHashCode() > GetHashCode()) ? 4 : 2));
if (otherNode.IsAncestorOf(this))
return DocumentPositions.Preceding | DocumentPositions.Contains;
if (otherNode.IsDescendantOf(this))
return DocumentPositions.Following | DocumentPositions.ContainedBy;
if (otherNode.IsPreceding(this))
return DocumentPositions.Preceding;
return DocumentPositions.Following;
}
public bool Contains(INode otherNode)
{
return this.IsInclusiveAncestorOf(otherNode);
}
public void Normalize()
{
for (int i = 0; i < _children.Length; i++) {
TextNode text = _children[i] as TextNode;
if (text != null) {
int length = text.Length;
if (length == 0) {
RemoveChild(text, false);
i--;
} else {
StringBuilder stringBuilder = Pool.NewStringBuilder();
TextNode sibling = text;
int end = i;
<>c__DisplayClass2 <>c__DisplayClass;
while ((sibling = (sibling.NextSibling as TextNode)) != null) {
stringBuilder.Append(sibling.Data);
end++;
Document owner = _owner;
Predicate<AngleSharp.Dom.Collections.Range> condition = (AngleSharp.Dom.Collections.Range m) => m.Head == sibling;
object action = <>c__DisplayClass.CS$<>9__CachedAnonymousMethodDelegate5;
if (action == null) {
Action<AngleSharp.Dom.Collections.Range> action2;
action = (action2 = delegate(AngleSharp.Dom.Collections.Range m) {
m.StartWith(text, length);
});
}
owner.ForEachRange(condition, (Action<AngleSharp.Dom.Collections.Range>)action);
Document owner2 = _owner;
Predicate<AngleSharp.Dom.Collections.Range> condition2 = (AngleSharp.Dom.Collections.Range m) => m.Tail == sibling;
object action3 = <>c__DisplayClass.CS$<>9__CachedAnonymousMethodDelegate8;
if (action3 == null) {
Action<AngleSharp.Dom.Collections.Range> action4;
action3 = (action4 = delegate(AngleSharp.Dom.Collections.Range m) {
m.EndWith(text, length);
});
}
owner2.ForEachRange(condition2, (Action<AngleSharp.Dom.Collections.Range>)action3);
Document owner3 = _owner;
Predicate<AngleSharp.Dom.Collections.Range> condition3 = delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Head == sibling.Parent)
return m.Start == end;
return false;
};
object action5 = <>c__DisplayClass.CS$<>9__CachedAnonymousMethodDelegate11;
if (action5 == null) {
Action<AngleSharp.Dom.Collections.Range> action6;
action5 = (action6 = delegate(AngleSharp.Dom.Collections.Range m) {
m.StartWith(text, length);
});
}
owner3.ForEachRange(condition3, (Action<AngleSharp.Dom.Collections.Range>)action5);
Document owner4 = _owner;
Predicate<AngleSharp.Dom.Collections.Range> condition4 = delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Tail == sibling.Parent)
return m.End == end;
return false;
};
object action7 = <>c__DisplayClass.CS$<>9__CachedAnonymousMethodDelegate14;
if (action7 == null) {
Action<AngleSharp.Dom.Collections.Range> action8;
action7 = (action8 = delegate(AngleSharp.Dom.Collections.Range m) {
m.EndWith(text, length);
});
}
owner4.ForEachRange(condition4, (Action<AngleSharp.Dom.Collections.Range>)action7);
length += sibling.Length;
}
text.Replace(text.Length, 0, stringBuilder.ToPool());
for (int num = end; num > i; num--) {
RemoveChild(_children[num], false);
}
}
} else if (_children[i].HasChildNodes) {
_children[i].Normalize();
}
}
}
public string LookupNamespaceUri(string prefix)
{
if (string.IsNullOrEmpty(prefix))
prefix = null;
return LocateNamespace(prefix);
}
public string LookupPrefix(string namespaceUri)
{
if (string.IsNullOrEmpty(namespaceUri))
return null;
return LocatePrefix(namespaceUri);
}
public bool IsDefaultNamespace(string namespaceUri)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
return LocateNamespace(null) == namespaceUri;
}
public virtual bool Equals(INode otherNode)
{
if (BaseUri != otherNode.BaseUri || NodeName != otherNode.NodeName || ChildNodes.Length != otherNode.ChildNodes.Length)
return false;
for (int i = 0; i < _children.Length; i++) {
if (!_children[i].Equals(otherNode.ChildNodes[i]))
return false;
}
return true;
}
protected virtual string LocateNamespace(string prefix)
{
if (_parent != null)
return _parent.LocateNamespace(prefix);
return null;
}
protected virtual string LocatePrefix(string namespaceUri)
{
if (_parent != null)
return _parent.LocatePrefix(namespaceUri);
return null;
}
internal void ChangeOwner(Document document)
{
Document owner = _owner;
if (_parent != null)
_parent.RemoveChild(this, false);
Owner = document;
NodeIsAdopted(owner);
}
internal void InsertNode(int index, Node node)
{
node.Parent = this;
_children.Insert(index, node);
}
internal void AddNode(Node node)
{
node.Parent = this;
_children.Add(node);
}
internal void RemoveNode(int index, Node node)
{
node.Parent = null;
_children.RemoveAt(index);
}
internal void ReplaceAll(Node node, bool suppressObservers)
{
if (node != null)
_owner.AdoptNode(node);
NodeList nodeList = new NodeList(_children);
NodeList nodeList2 = new NodeList();
if (node != null) {
if (node.NodeType == NodeType.DocumentFragment)
nodeList2.AddRange(node._children);
else
nodeList2.Add(node);
}
for (int i = 0; i < nodeList.Length; i++) {
RemoveChild(nodeList[i], true);
}
for (int j = 0; j < nodeList2.Length; j++) {
InsertBefore(nodeList2[j], null, true);
}
if (!suppressObservers)
_owner.QueueMutation(MutationRecord.ChildList(this, nodeList2, nodeList, null, null));
}
internal INode InsertBefore(Node newElement, Node referenceElement, bool suppressObservers)
{
int count = (newElement.NodeType != NodeType.DocumentFragment) ? 1 : newElement.ChildNodes.Length;
if (referenceElement != null) {
int childIndex = referenceElement.Index();
_owner.ForEachRange(delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Head == this)
return m.Start > childIndex;
return false;
}, delegate(AngleSharp.Dom.Collections.Range m) {
m.StartWith(this, m.Start + count);
});
_owner.ForEachRange(delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Tail == this)
return m.End > childIndex;
return false;
}, delegate(AngleSharp.Dom.Collections.Range m) {
m.EndWith(this, m.End + count);
});
}
if (newElement.NodeType == NodeType.Document || newElement.Contains(this))
throw new DomException(ErrorCode.HierarchyRequest);
NodeList nodeList = new NodeList();
int num = _children.Index(referenceElement);
if (num == -1)
num = _children.Length;
if (newElement._type == NodeType.DocumentFragment) {
int num2 = num;
int i = num;
while (newElement.HasChildNodes) {
Node node = newElement.ChildNodes[0];
newElement.RemoveChild(node, true);
InsertNode(num2, node);
AddNode(node);
num2++;
}
for (; i < num2; i++) {
Node node2 = _children[i];
nodeList.Add(node2);
NodeIsInserted(node2);
}
} else {
nodeList.Add(newElement);
InsertNode(num, newElement);
NodeIsInserted(newElement);
}
if (!suppressObservers)
_owner.QueueMutation(MutationRecord.ChildList(this, nodeList, null, _children[num - 1], referenceElement));
return newElement;
}
internal void RemoveChild(Node node, bool suppressObservers)
{
int index = _children.Index(node);
_owner.ForEachRange((AngleSharp.Dom.Collections.Range m) => m.Head.IsInclusiveDescendantOf(node), delegate(AngleSharp.Dom.Collections.Range m) {
m.StartWith(this, index);
});
_owner.ForEachRange((AngleSharp.Dom.Collections.Range m) => m.Tail.IsInclusiveDescendantOf(node), delegate(AngleSharp.Dom.Collections.Range m) {
m.EndWith(this, index);
});
_owner.ForEachRange(delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Head == this)
return m.Start > index;
return false;
}, delegate(AngleSharp.Dom.Collections.Range m) {
m.StartWith(this, m.Start - 1);
});
_owner.ForEachRange(delegate(AngleSharp.Dom.Collections.Range m) {
if (m.Tail == this)
return m.End > index;
return false;
}, delegate(AngleSharp.Dom.Collections.Range m) {
m.EndWith(this, m.End - 1);
});
Node node2 = (index > 0) ? _children[index - 1] : null;
if (!suppressObservers) {
NodeList nodeList = new NodeList();
nodeList.Add(node);
_owner.QueueMutation(MutationRecord.ChildList(this, null, nodeList, node2, node.NextSibling));
_owner.AddTransientObserver(node);
}
RemoveNode(index, node);
NodeIsRemoved(node, node2);
}
internal INode ReplaceChild(Node node, Node child, bool suppressObservers)
{
if (_type != NodeType.Document && _type != NodeType.DocumentFragment && _type != NodeType.Element)
throw new DomException(ErrorCode.HierarchyRequest);
if (node.IsHostIncludingInclusiveAncestor(this))
throw new DomException(ErrorCode.HierarchyRequest);
if (child.Parent != this)
throw new DomException(ErrorCode.NotFound);
NodeType nodeType = node.NodeType;
if (nodeType == NodeType.Element || nodeType == NodeType.Comment || nodeType == NodeType.Text || nodeType == NodeType.ProcessingInstruction || nodeType == NodeType.DocumentFragment || nodeType == NodeType.DocumentType) {
IDocument document = _parent as IDocument;
if (document != null) {
bool flag = false;
switch (node._type) {
case NodeType.DocumentType:
flag = (document.Doctype != child || child.IsPrecededByElement());
break;
case NodeType.Element:
flag = (document.DocumentElement != child || child.IsFollowedByDoctype());
break;
case NodeType.DocumentFragment: {
int elementCount = node.GetElementCount();
flag = (elementCount > 1 || node.HasTextNodes() || (elementCount == 1 && (document.DocumentElement != child || child.IsFollowedByDoctype())));
break;
}
}
if (flag)
throw new DomException(ErrorCode.HierarchyRequest);
}
Node nextSibling = child.NextSibling;
if (nextSibling == node)
nextSibling = node.NextSibling;
_owner.AdoptNode(node);
RemoveChild(child, true);
InsertBefore(node, nextSibling, true);
NodeList nodeList = new NodeList();
NodeList nodeList2 = new NodeList();
nodeList2.Add(child);
if (node._type == NodeType.DocumentFragment)
nodeList.AddRange(node._children);
else
nodeList.Add(node);
if (!suppressObservers)
_owner.QueueMutation(MutationRecord.ChildList(this, nodeList, nodeList2, child.PreviousSibling, nextSibling));
return child;
}
throw new DomException(ErrorCode.HierarchyRequest);
}
internal virtual void NodeIsAdopted(Document oldDocument)
{
}
internal virtual void NodeIsInserted(Node newNode)
{
}
internal virtual void NodeIsRemoved(Node removedNode, Node oldPreviousSibling)
{
}
protected static void CopyProperties(Node source, Node target, bool deep)
{
target._baseUri = source._baseUri;
if (deep) {
foreach (INode child in source._children) {
target.AddNode((Node)child.Clone(true));
}
}
}
public virtual string ToHtml()
{
return TextContent;
}
}
}