#region Copyright and License
//
// Fizzler - CSS Selector Engine for Microsoft .NET Framework
// Copyright (c) 2009 Atif Aziz, Colin Ramsay. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
#endregion
namespace Fizzler
{
#region Imports
using System;
using System.Collections.Generic;
using System.Linq;
#endregion
///
/// A selector generator implementation for an arbitrary document/element system.
///
public class SelectorGenerator : ISelectorGenerator
{
private readonly IEqualityComparer _equalityComparer;
private readonly Stack> _selectors;
///
/// Initializes a new instance of this object with an instance
/// of and the default equality
/// comparer that is used for determining if two elements are equal.
///
public SelectorGenerator(IElementOps ops) : this(ops, null) {}
///
/// Initializes a new instance of this object with an instance
/// of and an equality comparer
/// used for determining if two elements are equal.
///
public SelectorGenerator(IElementOps ops, IEqualityComparer equalityComparer)
{
if(ops == null) throw new ArgumentNullException("ops");
Ops = ops;
_equalityComparer = equalityComparer ?? EqualityComparer.Default;
_selectors = new Stack>();
}
///
/// Gets the selector implementation.
///
///
/// If the generation is not complete, this property returns the
/// last generated selector.
///
public Selector Selector { get; private set; }
///
/// Gets the instance that this object
/// was initialized with.
///
public IElementOps Ops { get; private set; }
///
/// Returns the collection of selector implementations representing
/// a group.
///
///
/// If the generation is not complete, this method return the
/// selectors generated so far in a group.
///
public IEnumerable> GetSelectors()
{
var selectors = _selectors;
var top = Selector;
return top == null
? selectors.Select(s => s)
: selectors.Concat(Enumerable.Repeat(top, 1));
}
///
/// Adds a generated selector.
///
protected void Add(Selector selector)
{
if(selector == null) throw new ArgumentNullException("selector");
var top = Selector;
Selector = top == null ? selector : (elements => selector(top(elements)));
}
///
/// Delimits the initialization of a generation.
///
public virtual void OnInit()
{
_selectors.Clear();
Selector = null;
}
///
/// Delimits a selector generation in a group of selectors.
///
public virtual void OnSelector()
{
if (Selector != null)
_selectors.Push(Selector);
Selector = null;
}
///
/// Delimits the closing/conclusion of a generation.
///
public virtual void OnClose()
{
var sum = GetSelectors().Aggregate((a, b) => (elements => a(elements).Concat(b(elements))));
var normalize = Ops.Descendant();
Selector = elements => sum(normalize(elements)).Distinct(_equalityComparer);
_selectors.Clear();
}
///
/// Generates a ID selector,
/// which represents an element instance that has an identifier that
/// matches the identifier in the ID selector.
///
public virtual void Id(string id)
{
Add(Ops.Id(id));
}
///
/// Generates a class selector,
/// which is an alternative when
/// representing the class attribute.
///
public virtual void Class(string clazz)
{
Add(Ops.Class(clazz));
}
///
/// Generates a type selector,
/// which represents an instance of the element type in the document tree.
///
public virtual void Type(NamespacePrefix prefix, string type)
{
Add(Ops.Type(prefix, type));
}
///
/// Generates a universal selector,
/// any single element in the document tree in any namespace
/// (including those without a namespace) if no default namespace
/// has been specified for selectors.
///
public virtual void Universal(NamespacePrefix prefix)
{
Add(Ops.Universal(prefix));
}
///
/// Generates an attribute selector
/// that represents an element with the given attribute
/// whatever the values of the attribute.
///
public virtual void AttributeExists(NamespacePrefix prefix, string name)
{
Add(Ops.AttributeExists(prefix, name));
}
///
/// Generates an attribute selector
/// that represents an element with the given attribute
/// and whose value is exactly .
///
public virtual void AttributeExact(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributeExact(prefix, name, value));
}
///
/// Generates an attribute selector
/// that represents an element with the given attribute
/// and whose value is a whitespace-separated list of words, one of
/// which is exactly .
///
public virtual void AttributeIncludes(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributeIncludes(prefix, name, value));
}
///
/// Generates an attribute selector
/// that represents an element with the given attribute ,
/// its value either being exactly or beginning
/// with immediately followed by "-" (U+002D).
///
public virtual void AttributeDashMatch(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributeDashMatch(prefix, name, value));
}
///
/// Generates an attribute selector
/// that represents an element with the attribute
/// whose value begins with the prefix .
///
public void AttributePrefixMatch(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributePrefixMatch(prefix, name, value));
}
///
/// Generates an attribute selector
/// that represents an element with the attribute
/// whose value ends with the suffix .
///
public void AttributeSuffixMatch(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributeSuffixMatch(prefix, name, value));
}
///
/// Generates an attribute selector
/// that represents an element with the attribute
/// whose value contains at least one instance of the substring .
///
public void AttributeSubstring(NamespacePrefix prefix, string name, string value)
{
Add(Ops.AttributeSubstring(prefix, name, value));
}
///
/// Generates a pseudo-class selector,
/// which represents an element that is the first child of some other element.
///
public virtual void FirstChild()
{
Add(Ops.FirstChild());
}
///
/// Generates a pseudo-class selector,
/// which represents an element that is the last child of some other element.
///
public virtual void LastChild()
{
Add(Ops.LastChild());
}
///
/// Generates a pseudo-class selector,
/// which represents an element that is the N-th child of some other element.
///
public virtual void NthChild(int a, int b)
{
Add(Ops.NthChild(a, b));
}
///
/// Generates a pseudo-class selector,
/// which represents an element that has a parent element and whose parent
/// element has no other element children.
///
public virtual void OnlyChild()
{
Add(Ops.OnlyChild());
}
///
/// Generates a pseudo-class selector,
/// which represents an element that has no children at all.
///
public virtual void Empty()
{
Add(Ops.Empty());
}
///
/// Generates a combinator,
/// which represents a childhood relationship between two elements.
///
public virtual void Child()
{
Add(Ops.Child());
}
///
/// Generates a combinator,
/// which represents a relationship between two elements where one element is an
/// arbitrary descendant of some ancestor element.
///
public virtual void Descendant()
{
Add(Ops.Descendant());
}
///
/// Generates a combinator,
/// which represents elements that share the same parent in the document tree and
/// where the first element immediately precedes the second element.
///
public virtual void Adjacent()
{
Add(Ops.Adjacent());
}
///
/// Generates a combinator,
/// which separates two sequences of simple selectors. The elements represented
/// by the two sequences share the same parent in the document tree and the
/// element represented by the first sequence precedes (not necessarily
/// immediately) the element represented by the second one.
///
public virtual void GeneralSibling()
{
Add(Ops.GeneralSibling());
}
///
/// Generates a pseudo-class selector,
/// which represents an element that is the N-th child from bottom up of some other element.
///
public void NthLastChild(int a, int b)
{
Add(Ops.NthLastChild(a, b));
}
}
}