ComplexSelector
Represents a complex selector, i.e. one or more compound selectors
separated by combinators.
using AngleSharp.Css;
using AngleSharp.Extensions;
using AngleSharp.Parser.Css;
using System;
using System.Collections.Generic;
using System.IO;
namespace AngleSharp.Dom.Css
{
internal sealed class ComplexSelector : CssNode, ISelector, ICssNode, IStyleFormattable
{
private struct CombinatorSelector
{
public string Delimiter;
public Func<IElement, IEnumerable<IElement>> Transform;
public ISelector Selector;
}
private readonly List<CombinatorSelector> _selectors;
public Priority Specifity {
get {
Priority priority = default(Priority);
int count = _selectors.Count;
for (int i = 0; i < count; i++) {
priority += _selectors[i].Selector.Specifity;
}
return priority;
}
}
public string Text => this.ToCss();
public int Length => _selectors.Count;
public bool IsReady { get; set; }
public ComplexSelector()
{
_selectors = new List<CombinatorSelector>();
}
public override void ToCss(TextWriter writer, IStyleFormatter formatter)
{
if (_selectors.Count > 0) {
int num = _selectors.Count - 1;
for (int i = 0; i < num; i++) {
writer.Write(_selectors[i].Selector.Text);
writer.Write(_selectors[i].Delimiter);
}
writer.Write(_selectors[num].Selector.Text);
}
}
public bool Match(IElement element)
{
int num = _selectors.Count - 1;
if (_selectors[num].Selector.Match(element)) {
if (num <= 0)
return true;
return MatchCascade(num - 1, element);
}
return false;
}
public void ConcludeSelector(ISelector selector)
{
if (!IsReady) {
_selectors.Add(new CombinatorSelector {
Selector = selector,
Transform = null,
Delimiter = null
});
IsReady = true;
}
}
public void AppendSelector(ISelector selector, CssCombinator combinator)
{
if (!IsReady)
_selectors.Add(new CombinatorSelector {
Selector = combinator.Change(selector),
Transform = combinator.Transform,
Delimiter = combinator.Delimiter
});
}
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;
}
}
}