ComplexSelector
Represents a complex selector.
One or more compound selectors separated by combinators.
using AngleSharp.Css;
using AngleSharp.Extensions;
using AngleSharp.Html;
using AngleSharp.Parser.Css;
using System;
using System.Collections.Generic;
using System.Text;
namespace AngleSharp.Dom.Css
{
internal sealed class ComplexSelector : ISelector
{
private struct CombinatorSelector
{
public char delimiter;
public Func<IElement, IEnumerable<IElement>> transform;
public ISelector selector;
}
private readonly List<CombinatorSelector> selectors;
public Priority Specifity {
get {
Priority priority = default(Priority);
for (int i = 0; i < selectors.Count; i++) {
priority += selectors[i].selector.Specifity;
}
return priority;
}
}
public string Text {
get {
StringBuilder stringBuilder = Pool.NewStringBuilder();
if (selectors.Count > 0) {
int num = selectors.Count - 1;
for (int i = 0; i < num; i++) {
stringBuilder.Append(selectors[i].selector.Text).Append(selectors[i].delimiter);
}
stringBuilder.Append(selectors[num].selector.Text);
}
return stringBuilder.ToPool();
}
}
public int Length => selectors.Count;
public bool IsReady { get; set; }
public ComplexSelector()
{
selectors = new List<CombinatorSelector>();
}
public bool Match(IElement element)
{
int num = selectors.Count - 1;
if (selectors[num].selector.Match(element)) {
if (num > 0)
return MatchCascade(num - 1, element);
return true;
}
return false;
}
public ComplexSelector ConcludeSelector(ISelector selector)
{
if (!IsReady) {
selectors.Add(new CombinatorSelector {
selector = selector,
transform = null
});
IsReady = true;
}
return this;
}
public ComplexSelector AppendSelector(ISelector selector, CssCombinator combinator)
{
if (IsReady)
return this;
Func<IElement, IEnumerable<IElement>> func = null;
char delimiter;
switch (combinator) {
case CssCombinator.Child:
delimiter = '>';
func = ((IElement el) => Single(el.ParentElement));
break;
case CssCombinator.AdjacentSibling:
delimiter = '+';
func = ((IElement el) => Single(el.PreviousElementSibling));
break;
case CssCombinator.Descendent:
delimiter = ' ';
func = delegate(IElement el) {
List<IElement> list2 = new List<IElement>();
for (IElement parentElement2 = el.ParentElement; parentElement2 != null; parentElement2 = parentElement2.ParentElement) {
list2.Add(parentElement2);
}
return list2;
};
break;
case CssCombinator.Sibling:
delimiter = '~';
func = delegate(IElement el) {
IElement parentElement = el.ParentElement;
if (parentElement == null)
return new IElement[0];
List<IElement> list = new List<IElement>();
foreach (INode childNode in parentElement.ChildNodes) {
IElement element = childNode as IElement;
if (element != null) {
if (element == el)
return list;
list.Add(element);
}
}
return list;
};
break;
case CssCombinator.Namespace: {
string prefix = selector.Text;
delimiter = '|';
func = ((IElement el) => Single(el));
selector = new SimpleSelector((IElement el) => MatchesCssNamespace(el, prefix), Priority.Zero, prefix);
break;
}
default:
return this;
}
selectors.Add(new CombinatorSelector {
selector = selector,
transform = func,
delimiter = delimiter
});
return this;
}
public ComplexSelector ClearSelectors()
{
IsReady = false;
selectors.Clear();
return this;
}
private static bool MatchesCssNamespace(IElement el, string prefix)
{
if (prefix == "*")
return true;
string a = el.GetAttribute(Namespaces.XmlNsPrefix) ?? el.NamespaceUri;
if (prefix == string.Empty)
return a == string.Empty;
return a == GetCssNamespace(el, prefix);
}
private static string GetCssNamespace(IElement el, string prefix)
{
return el.Owner.StyleSheets.LocateNamespace(prefix) ?? el.LocateNamespace(prefix);
}
private bool MatchCascade(int pos, IElement element)
{
foreach (IElement item in selectors[pos].transform(element)) {
if (selectors[pos].selector.Match(item) && (pos == 0 || MatchCascade(pos - 1, item)))
return true;
}
return false;
}
private static IEnumerable<IElement> Single(IElement element)
{
if (element != null)
yield return element;
}
}
}