using System;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.Text;
using System.Collections;
#if TEST
using NUnit.Framework;
#endif
namespace SharpVectors.Dom.Css{
#region Public enums
internal enum XPathSelectorStatus
{
Start, Parsed, Compiled, Error
}
#endregion
public class XPathSelector
{
#region Static members
internal static Regex reSelector = new Regex(CssStyleRule.sSelector);
#endregion
#region Constructors
public XPathSelector(string selector) : this(selector, new Hashtable())
{
}
public XPathSelector(string selector, Hashtable namespaceTable)
{
CssSelector = selector.Trim();
NsTable = namespaceTable;
}
#endregion
#region Private fields
private int _specificity = 0;
private Hashtable NsTable;
private string sXpath;
private XPathExpression xpath = null;
#endregion
#region Internal properties
/// <summary>
/// Only used for testing!
/// </summary>
public string XPath
{
get
{
if(Status == XPathSelectorStatus.Start)
{
GetXPath(null);
}
return sXpath;
}
}
public int Specificity
{
get
{
if(Status == XPathSelectorStatus.Start)
{
GetXPath(null);
}
if(Status != XPathSelectorStatus.Error) return _specificity;
else return 0;
}
}
internal XPathSelectorStatus Status = XPathSelectorStatus.Start;
internal string CssSelector;
#endregion
#region Private methods
private void AddSpecificity(int a, int b, int c)
{
_specificity += a*100 + b*10 + c;
}
private string NsToXPath(Match match)
{
string r = String.Empty;
Group g = match.Groups["ns"];
if(g != null && g.Success)
{
string prefix = g.Value.TrimEnd(new char[]{'|'});
if(prefix.Length == 0)
{
// a element in no namespace
r += "[namespace-uri()='']";
}
else if(prefix == "*")
{
// do nothing, any or no namespace is okey
}
else if(NsTable.ContainsKey(prefix))
{
r += "[namespace-uri()='" + NsTable[prefix] + "']";
}
else
{
//undeclared namespace => invalid CSS selector
r += "[false]";
}
}
else if(NsTable.ContainsKey(String.Empty))
{
//if no default namespace has been specified, this is equivalent to *|E. Otherwise it is equivalent to ns|E where ns is the default namespace.
r += "[namespace-uri()='" + NsTable[String.Empty] + "']";
}
return r;
}
private string TypeToXPath(Match match)
{
string r = String.Empty;
Group g = match.Groups["type"];
string s = g.Value;
if(!g.Success || s=="*") r = String.Empty;
else
{
r = "[local-name()='" + s + "']";
AddSpecificity(0, 0, 1);
}
return r;
}
private string ClassToXPath(Match match)
{
string r = String.Empty;
Group g = match.Groups["class"];
foreach(Capture c in g.Captures)
{
r += "[contains(concat(' ',@class,' '),' " + c.Value.Substring(1) + " ')]";
AddSpecificity(0, 1, 0);
}
return r;
}
private string IdToXPath(Match match)
{
string r = String.Empty;
Group g = match.Groups["id"];
if(g.Success)
{
// r = "[id('" + g.Value.Substring(1) + "')]";
r = "[@id='" + g.Value.Substring(1) + "']";
AddSpecificity(1, 0, 0);
}
return r;
}
private string GetAttributeMatch(string attSelector)
{
string fullAttName = attSelector.Trim();
int pipePos = fullAttName.IndexOf("|");
string attMatch = String.Empty;
if(pipePos == -1 || pipePos == 0)
{
// att or |att => should be in the undeclared namespace
string attName = fullAttName.Substring(pipePos+1);
attMatch = "@" + attName;
}
else if(fullAttName.StartsWith("*|"))
{
// *|att => in any namespace (undeclared or declared)
attMatch = "@*[local-name()='" + fullAttName.Substring(2) + "']";
}
else
{
// ns|att => must macht a declared namespace
string ns = fullAttName.Substring(0, pipePos);
string attName = fullAttName.Substring(pipePos+1);
if(NsTable.ContainsKey(ns))
{
attMatch = "@" + ns + ":" + attName;
}
else
{
// undeclared namespace => selector should fail
attMatch = "false";
}
}
return attMatch;
}
private string PredicatesToXPath(Match match)
{
string r = String.Empty;
Group g = match.Groups["attributecheck"];
foreach(Capture c in g.Captures)
{
r += "[" + GetAttributeMatch(c.Value) + "]";
AddSpecificity(0, 1, 0);
}
g = match.Groups["attributevaluecheck"];
Regex reAttributeValueCheck = new Regex("^" + CssStyleRule.attributeValueCheck + "?$");
foreach(Capture c in g.Captures)
{
Match valueCheckMatch = reAttributeValueCheck.Match(c.Value);
string attName = valueCheckMatch.Groups["attname"].Value;
string attMatch = GetAttributeMatch(attName);
string eq = valueCheckMatch.Groups["eqtype"].Value; // ~,^,$,*,|,nothing
string attValue = valueCheckMatch.Groups["attvalue"].Value;
switch(eq)
{
case "":
// [foo="bar"] => [@foo='bar']
r += "[" + attMatch + "='" + attValue + "']";
break;
case "~":
// [foo~="bar"]
// an E element whose "foo" attribute value is a list of space-separated values, one of which is exactly equal to "bar"
r += "[contains(concat(' '," + attMatch + ",' '),' " + attValue + " ')]";
break;
case "^":
// [foo^="bar"]
// an E element whose "foo" attribute value begins exactly with the string "bar"
r += "[starts-with(" + attMatch + ",'" + attValue + "')]";
break;
case "$":
// [foo$="bar"]
// an E element whose "foo" attribute value ends exactly with the string "bar"
int a = attValue.Length - 1;
r += "[substring(" + attMatch + ",string-length(" + attMatch + ")-" + a + ")='" + attValue + "']";
break;
case "*":
// [foo*="bar"]
// an E element whose "foo" attribute value contains the substring "bar"
r += "[contains(" + attMatch + ",'" + attValue + "')]";
break;
case "|":
// [hreflang|="en"]
// an E element whose "hreflang" attribute has a hyphen-separated list of values beginning (from the left) with "en"
r += "[" + attMatch + "='" + attValue + "' or starts-with(" + attMatch + ",'" + attValue + "-')]";
break;
}
AddSpecificity(0, 1, 0);
}
return r;
}
private string PseudoClassesToXPath(Match match, XPathNavigator nav)
{
int specificityA = 0;
int specificityB = 1;
int specificityC = 0;
string r = String.Empty;
Group g = match.Groups["pseudoclass"];
foreach(Capture c in g.Captures)
{
Regex reLang = new Regex(@"^lang\(([A-Za-z\-]+)\)$");
Regex reContains = new Regex("^contains\\((\"|\')?(?<stringvalue>.*?)(\"|\')?\\)$");
string s = @"^(?<type>(nth-child)|(nth-last-child)|(nth-of-type)|(nth-last-of-type))\(\s*";
s += @"(?<exp>(odd)|(even)|(((?<a>[\+-]?\d*)n)?(?<b>[\+-]?\d+)?))";
s += @"\s*\)$";
Regex reNth = new Regex(s);
string p = c.Value.Substring(1);
if(p == "root")
{
r += "[not(parent::*)]";
}
else if(p.StartsWith("not"))
{
string expr = p.Substring(4, p.Length-5);
XPathSelector sel = new XPathSelector(expr, NsTable);
string xpath = sel.XPath;
if(xpath != null && xpath.Length>3)
{
// remove *[ and ending ]
xpath = xpath.Substring(2, xpath.Length-3);
r += "[not(" + xpath + ")]";
int specificity = sel.Specificity;
// specificity = 123
specificityA = (int)Math.Floor((double) specificity / 100);
specificity -= specificityA*100;
// specificity = 23
specificityB = (int)Math.Floor((double) (specificity) / 10);
specificity -= specificityB * 10;
// specificity = 3
specificityC = specificity;
}
}
else if(p == "first-child")
{
r += "[count(preceding-sibling::*)=0]";
}
else if(p == "last-child")
{
r += "[count(following-sibling::*)=0]";
}
else if(p == "only-child")
{
r += "[count(../*)=1]";
}
else if(p == "only-of-type")
{
r += "[false]";
}
else if(p == "empty")
{
r += "[not(child::*) and not(text())]";
}
else if(p == "target")
{
r += "[false]";
}
else if(p == "first-of-type")
{
r += "[false]";
//r += "[.=(../*[local-name='roffe'][position()=1])]";
}
else if(reLang.IsMatch(p))
{
r += "[lang('" + reLang.Match(p).Groups[1].Value + "')]";
}
else if(reContains.IsMatch(p))
{
r += "[contains(string(.),'" + reContains.Match(p).Groups["stringvalue"].Value + "')]";
}
else if(reNth.IsMatch(p))
{
Match m = reNth.Match(p);
string type = m.Groups["type"].Value;
string exp = m.Groups["exp"].Value;
int a = 0;
int b = 0;
if(exp == "odd")
{
a = 2;
b = 1;
}
else if(exp == "even")
{
a = 2;
b = 0;
}
else
{
string v = m.Groups["a"].Value;
if(v.Length == 0) a = 1;
else if(v.Equals("-")) a = -1;
else a = Int32.Parse(v);
if(m.Groups["b"].Success) b = Int32.Parse(m.Groups["b"].Value);
}
if(type.Equals("nth-child") || type.Equals("nth-last-child"))
{
string axis;
if(type.Equals("nth-child")) axis = "preceding-sibling";
else axis = "following-sibling";
if(a == 0)
{
r += "[count(" + axis + "::*)+1=" + b + "]";
}
else
{
r += "[((count(" + axis + "::*)+1-" + b + ") mod " + a + "=0)and((count(" + axis + "::*)+1-" + b + ") div " + a + ">=0)]";
}
}
}
AddSpecificity(specificityA, specificityB, specificityC);
}
return r;
}
private void SeperatorToXPath(Match match, StringBuilder xpath, string cur)
{
Group g = match.Groups["seperator"];
if(g.Success)
{
string s = g.Value.Trim();
if(s.Length == 0) cur += "//*";
else if(s == ">") cur += "/*";
else if(s == "+" || s == "~")
{
xpath.Append("[preceding-sibling::*");
if(s == "+")
{
xpath.Append("[position()=1]");
}
xpath.Append(cur);
xpath.Append("]");
cur = String.Empty;
}
}
xpath.Append(cur);
}
#endregion
#region Internal methods
internal void GetXPath(XPathNavigator nav)
{
this._specificity = 0;
StringBuilder xpath = new StringBuilder("*");
Match match = reSelector.Match(CssSelector);
while(match.Success)
{
if(match.Success && match.Value.Length > 0)
{
string x = String.Empty;
x += NsToXPath(match);
x += TypeToXPath(match);
x += ClassToXPath(match);
x += IdToXPath(match);
x += PredicatesToXPath(match);
x += PseudoClassesToXPath(match, nav);
SeperatorToXPath(match, xpath, x);
}
match = match.NextMatch();
}
if(nav != null) Status = XPathSelectorStatus.Parsed;
sXpath = xpath.ToString();
}
private XmlNamespaceManager GetNSManager()
{
XmlNamespaceManager nsman = new XmlNamespaceManager(new NameTable());
IDictionaryEnumerator dicEnum = NsTable.GetEnumerator();
while(dicEnum.MoveNext())
{
nsman.AddNamespace((string)dicEnum.Key, (string)dicEnum.Value);
}
return nsman;
}
internal void Compile(XPathNavigator nav)
{
if(Status == XPathSelectorStatus.Start)
{
GetXPath(nav);
}
if(Status == XPathSelectorStatus.Parsed)
{
xpath = nav.Compile(sXpath);
xpath.SetContext(GetNSManager());
Status = XPathSelectorStatus.Compiled;
}
}
public bool Matches(XPathNavigator nav)
{
if(Status != XPathSelectorStatus.Compiled)
{
Compile(nav);
}
if(Status == XPathSelectorStatus.Compiled)
{
try
{
return nav.Matches(xpath);
}
catch
{
return false;
}
}
else
{
return false;
}
}
#endregion
#region Unit tests
#if TEST
[TestFixture]
public class XPathSelectorTests
{
CssXmlDocument doc = new CssXmlDocument();
XPathNavigator a;
XPathNavigator b;
XPathNavigator c;
XPathNavigator d;
XPathNavigator e;
XPathNavigator f;
XPathNavigator g;
Hashtable nsTable = new Hashtable();
#region Testdata
string testXml =
"<?xml version='1.0' encoding='UTF-8' standalone='no'?>" +
"<!DOCTYPE a [" +
" <!ELEMENT a (#PCDATA | b | e | f | ns:g)*>" +
" <!ATTLIST a" +
" xmlns:ns CDATA #REQUIRED" +
">" +
" <!ELEMENT b (#PCDATA | c)*>" +
" <!ATTLIST b" +
" id CDATA #REQUIRED" +
" att CDATA #REQUIRED" +
" class CDATA #REQUIRED" +
" ns:nsatt CDATA #REQUIRED" +
">" +
" <!ELEMENT c (#PCDATA | d)*>" +
" <!ATTLIST c" +
" id ID #REQUIRED" +
" xml:lang CDATA #REQUIRED" +
" hreflang CDATA #REQUIRED" +
">" +
" <!ELEMENT d (#PCDATA)>" +
" <!ELEMENT e EMPTY>" +
" <!ELEMENT f EMPTY>" +
" <!ELEMENT ns:g EMPTY>" +
"]>" +
"<a xmlns:ns='http://www.sharpvectors.org'>asdsa" +
" <b id='mrB' att='attvalue' class='class1 class2 class3' ns:nsatt='foo'>" +
" <c id='mrC' xml:lang='se-SV' hreflang='sv-FI'>kjjkkjjk" +
" <d>some text</d>" +
" </c>sdfsdf" +
" </b>" +
" <e/>" +
" <e/>" +
" <e/>" +
" <f/>" +
" <ns:g/>" +
"</a>";
#endregion
#region Setup
[SetUp]
public void Init()
{
doc.LoadXml(testXml);
if(!nsTable.ContainsKey("ns")) nsTable.Add("ns", "http://www.sharpvectors.org");
a = doc.SelectSingleNode("//a").CreateNavigator();
b = doc.SelectSingleNode("//b").CreateNavigator();
c = doc.SelectSingleNode("//c").CreateNavigator();
d = doc.SelectSingleNode("//d").CreateNavigator();
e = doc.SelectSingleNode("//e").CreateNavigator();
f = doc.SelectSingleNode("//f").CreateNavigator();
g = doc.SelectSingleNode("//*[local-name()='g']").CreateNavigator();
}
#endregion
[Test]
public void TestSpecificity()
{
Assert.AreEqual(0, new XPathSelector("*").Specificity, "*");
Assert.AreEqual(1, new XPathSelector("LI").Specificity, "LI");
Assert.AreEqual(1, new XPathSelector("ns|LI", nsTable).Specificity, "ns|LI");
Assert.AreEqual(2, new XPathSelector("UL LI").Specificity, "UL LI");
Assert.AreEqual(3, new XPathSelector("UL OL+LI").Specificity, "UL OL+LI");
Assert.AreEqual(11, new XPathSelector("H1 + *[REL=up]").Specificity, "H1 + *[REL=up]");
Assert.AreEqual(13, new XPathSelector("UL OL LI.red").Specificity, "UL OL LI.red");
Assert.AreEqual(21, new XPathSelector("LI.red.level").Specificity, "LI.red.level");
Assert.AreEqual(100 , new XPathSelector("#x34y").Specificity, "#x34y");
Assert.AreEqual(101, new XPathSelector("#s12:not(FOO)").Specificity, "#s12:not(FOO)");
}
#region Type matching
[Test]
public void TestUniversalMatching()
{
Assert.IsTrue(new XPathSelector("*").Matches(a));
Assert.IsTrue(new XPathSelector("*").Matches(b));
}
[Test]
public void TestTypeMatching()
{
Assert.IsTrue(new XPathSelector("a").Matches(a));
Assert.IsTrue(new XPathSelector("b").Matches(b));
Assert.IsTrue(!(new XPathSelector("a").Matches(b)));
Assert.IsTrue(!(new XPathSelector("A").Matches(a)));
}
#endregion
#region Relation matching
[Test]
public void TestDescendantMatching()
{
Assert.IsTrue(new XPathSelector("a b").Matches(b), "a b");
Assert.IsTrue(new XPathSelector("a c").Matches(c), "a c");
Assert.IsTrue(new XPathSelector("b d").Matches(d), "b d");
Assert.IsTrue(!(new XPathSelector("b e").Matches(e)), "b e");
Assert.IsTrue(!(new XPathSelector("b a").Matches(a)), "b a");
}
[Test]
public void TestChildMatching()
{
Assert.IsTrue(new XPathSelector("a>b").Matches(b), "a>b");
Assert.IsTrue(!(new XPathSelector("a >c").Matches(c)), "a >c");
Assert.IsTrue(!(new XPathSelector("a > d").Matches(d)), "a > d");
Assert.IsTrue(new XPathSelector("b> c").Matches(c), "b> c");
}
[Test]
public void TestDirectAdjacentMatching()
{
Assert.IsTrue(new XPathSelector("b +e").Matches(e), "b +e");
Assert.IsTrue(new XPathSelector("e+f").Matches(f), "e+f");
Assert.IsTrue(!(new XPathSelector("b+f").Matches(f)), "b+f");
}
[Test]
public void TestIndirectAdjacentMatching()
{
Assert.IsTrue(new XPathSelector("b ~e").Matches(e), "b ~e");
Assert.IsTrue(new XPathSelector("e~f").Matches(f), "e~f");
Assert.IsTrue(new XPathSelector("b~f").Matches(f), "b~f");
Assert.IsTrue(!(new XPathSelector("f~e").Matches(e)), "f~e");
}
#endregion
#region Class, Id and namespace matching
[Test]
public void TestClassMatching()
{
Assert.IsTrue(new XPathSelector(".class1").Matches(b), ".class1");
Assert.IsTrue(new XPathSelector(".class2").Matches(b), ".class2");
Assert.IsTrue(new XPathSelector("b.class3").Matches(b), "b.class3");
Assert.IsTrue(new XPathSelector(".class3.class2").Matches(b), ".class3.class2");
Assert.IsTrue(new XPathSelector(".class1.class2.class3").Matches(b), ".class1.class2.class3");
Assert.IsTrue(!(new XPathSelector(".class1").Matches(c)), ".class1");
Assert.IsTrue(!(new XPathSelector(".dummy").Matches(b)), ".dummy");
}
[Test]
public void TestIdMatching()
{
Assert.IsTrue(new XPathSelector("#mrC").Matches(c), "#mrC");
Assert.IsTrue(new XPathSelector("c#mrC").Matches(c), "c#mrC");
}
[Test]
[Ignore("DTD support disabled")]
public void TestIdMatchingNoDtd()
{
// id on <b> is not defined in the DTD as ID
Assert.IsTrue(!(new XPathSelector("#mrB").Matches(b)), "#mrB");
}
[Test]
public void TestNsMatching()
{
Assert.IsTrue(new XPathSelector("ns|g", nsTable).Matches(g), "ns|g");
Assert.IsTrue(new XPathSelector("*|g", nsTable).Matches(g), "*|g");
Assert.IsTrue(new XPathSelector("|a", nsTable).Matches(a), "|a");
Assert.IsTrue(new XPathSelector("*|a", nsTable).Matches(a), "*|a");
Assert.IsTrue(!(new XPathSelector("dummy|g", nsTable).Matches(g)), "dummy|g");
Assert.IsTrue(!(new XPathSelector("ns|a", nsTable).Matches(a)), "ns|a");
}
#endregion
#region Attribute matching
[Test]
public void TestAttributeExistsMatching()
{
Assert.IsTrue(new XPathSelector("b[att]").Matches(b), "b[att]");
Assert.IsTrue(new XPathSelector("b[ns|nsatt]", nsTable).Matches(b), "b[ns|nsatt]");
Assert.IsTrue(new XPathSelector("b[*|nsatt]", nsTable).Matches(b), "b[*|nsatt]");
Assert.IsTrue(new XPathSelector("b[*|att]", nsTable).Matches(b), "b[*|att]");
Assert.IsTrue(new XPathSelector("b[|att]", nsTable).Matches(b), "b[|att]");
Assert.IsTrue(!(new XPathSelector("b[|nsatt]", nsTable).Matches(b)), "b[|nsatt]");
Assert.IsTrue(!(new XPathSelector("b[nsatt]", nsTable).Matches(b)), "b[nsatt]");
Assert.IsTrue(new XPathSelector("b[ att][ class ]").Matches(b), "b[ att][ class ]");
Assert.IsTrue(!(new XPathSelector("b[att][dummy]").Matches(b)), "b[att][dummy]");
Assert.IsTrue(!(new XPathSelector("b[dummy]").Matches(b)), "b[dummy]");
}
[Test]
public void TestAttributeValueMatching()
{
Assert.IsTrue(new XPathSelector("b[att=attvalue]").Matches(b), "b[att=attvalue]");
Assert.IsTrue(new XPathSelector("b[att= 'attvalue' ]").Matches(b), "b[att= 'attvalue' ]");
Assert.IsTrue(new XPathSelector("b[ att =\"attvalue\"]").Matches(b), "b[ att =\"attvalue\"]");
Assert.IsTrue(new XPathSelector("b[ns|nsatt='foo']", nsTable).Matches(b), "b[ns|nsatt='foo']");
Assert.IsTrue(new XPathSelector("b[*|nsatt=foo]", nsTable).Matches(b), "b[*|nsatt=foo]");
Assert.IsTrue(new XPathSelector("b[*|att=attvalue]", nsTable).Matches(b), "b[*|att=attvalue]");
Assert.IsTrue(new XPathSelector("b[|att=\"attvalue\"]", nsTable).Matches(b), "b[|att=\"attvalue\"]");
Assert.IsTrue(!(new XPathSelector("b[|nsatt=foo]", nsTable).Matches(b)), "b[|nsatt=foo]");
Assert.IsTrue(!(new XPathSelector("b[nsatt=foo]", nsTable).Matches(b)), "b[nsatt=foo]");
Assert.IsTrue(!(new XPathSelector("b[att=dummy]").Matches(b)), "b[att=dummy]");
}
[Test]
public void TestAttributeValueSpaceSeparatedMatching()
{
Assert.IsTrue(new XPathSelector("b[class~=class2]").Matches(b), "b[class~=class2]");
Assert.IsTrue(new XPathSelector("b[class~=class3]").Matches(b), "b[class~=class3]");
Assert.IsTrue(new XPathSelector("b[class~=class2][class~=class3]").Matches(b), "b[class~=class2][class~=class3]");
Assert.IsTrue(!(new XPathSelector("b[class~=dummy]").Matches(b)), "b[class~=dummy]");
}
[Test]
public void TestAttributeValueBeginMatching()
{
Assert.IsTrue(new XPathSelector("b[att^=attv]").Matches(b), "b[att^=attv]");
Assert.IsTrue(new XPathSelector("b[att^=attvalue]").Matches(b), "b[att^=attvalue]");
Assert.IsTrue(!(new XPathSelector("b[att^=dummy]").Matches(b)), "b[att^=dummy]");
Assert.IsTrue(!(new XPathSelector("b[att^=ttv]").Matches(b)), "b[att^=ttv]");
}
[Test]
public void TestAttributeValueEndMatching()
{
Assert.IsTrue(new XPathSelector("b[att$=lue]").Matches(b), "b[att$=lue]");
Assert.IsTrue(new XPathSelector("b[att$=attvalue]").Matches(b), "b[att$=attvalue]");
Assert.IsTrue(!(new XPathSelector("b[att$=dummy]").Matches(b)), "b[att$=dummy]");
Assert.IsTrue(!(new XPathSelector("b[att$=ttv]").Matches(b)), "b[att$=ttv]");
}
[Test]
public void TestAttributeValueSubStringMatching()
{
Assert.IsTrue(new XPathSelector("b[att*=lue]").Matches(b), "b[att*=lue]");
Assert.IsTrue(new XPathSelector("b[att*=attvalue]").Matches(b), "b[att*=attvalue]");
Assert.IsTrue(new XPathSelector("b[att*=attv]").Matches(b), "b[att*=attv]");
Assert.IsTrue(!(new XPathSelector("b[att*=dummy]").Matches(b)), "b[att*=dummy]");
}
[Test]
public void TestAttributeValueHyphenMatching()
{
Assert.IsTrue(new XPathSelector("[hreflang|=sv]").Matches(c), "[hreflang|=sv]");
Assert.IsTrue(!(new XPathSelector("[hreflang|=sv]").Matches(b)), "[hreflang|=sv]");
}
#endregion
#region Misc
/* Not supported
[Test]
public void TestTargetMatching()
{
Console.WriteLine(doc.Url);
}
*/
[Test]
public void TestLangMatching()
{
Assert.IsTrue(new XPathSelector("c:lang(se-SV)").Matches(c), "c:lang(se-SV)");
Assert.IsTrue(new XPathSelector("c:lang(se)").Matches(c), "c:lang(se)");
Assert.IsTrue(new XPathSelector("d:lang(se)").Matches(d), "d:lang(se)");
Assert.IsTrue(!(new XPathSelector("c:lang(se-FI)").Matches(c)), "c:lang(se-FI)");
Assert.IsTrue(!(new XPathSelector("c:lang(en)").Matches(c)), "c:lang(en)");
}
[Test]
public void TestNotMatching()
{
Assert.IsTrue(new XPathSelector("c:not(:root)").Matches(c), "c:not(:root))");
Assert.IsTrue(new XPathSelector("a:not(.kalle)").Matches(a), "a:not(.kalle))");
Assert.IsTrue(new XPathSelector("*:not(b)").Matches(a), "*:not(b)");
Assert.IsTrue(new XPathSelector("*:not(b)").Matches(g), "*:not(b)");
Assert.IsTrue(!(new XPathSelector("*:not(b)").Matches(b)), "*:not(b)");
Assert.IsTrue(!(new XPathSelector("a:not(:root)").Matches(a)), "a:not(:root))");
}
[Test]
public void TestRootMatching()
{
Assert.IsTrue(new XPathSelector("a:root").Matches(a), "a:root");
Assert.IsTrue(!(new XPathSelector("b:root").Matches(b)), "b:root");
}
[Test]
public void TestContainsMatching()
{
Assert.IsTrue(new XPathSelector("d:contains(some)").Matches(d), "d:contains(some)");
Assert.IsTrue(new XPathSelector("d:contains('me te')").Matches(d), "d:contains('me te')");
Assert.IsTrue(new XPathSelector("d:contains(\"me text\")").Matches(d), "d:contains(\"me text\")");
Assert.IsTrue(!(new XPathSelector("d:contains(dummy)").Matches(d)), "d:contains(dummy)");
}
[Test]
public void TestOnlyChildMatching()
{
Assert.IsTrue(new XPathSelector("c:only-child").Matches(c), "c:only-child");
Assert.IsTrue(new XPathSelector("d:only-child").Matches(d), "d:only-child");
Assert.IsTrue(!(new XPathSelector("b:only-child").Matches(b)), "b:only-child");
Assert.IsTrue(!(new XPathSelector("e:only-child").Matches(e)), "e:only-child");
}
[Test]
public void TestEmptyMatching()
{
Assert.IsTrue(new XPathSelector("e:empty").Matches(e), "e:empty");
Assert.IsTrue(!(new XPathSelector("b:empty").Matches(b)), "b:empty");
Assert.IsTrue(!(new XPathSelector("d:empty").Matches(d)), "d:empty");
}
#endregion
#region Position matching
[Test]
public void TestFirstChildMatching()
{
Assert.IsTrue(new XPathSelector("b:first-child").Matches(b), "b:first-child");
Assert.IsTrue(new XPathSelector("d:first-child").Matches(d), "d:first-child");
Assert.IsTrue(!(new XPathSelector("e:first-child").Matches(e)), "e:first-child");
}
[Test]
public void TestLastChildMatching()
{
Assert.IsTrue(new XPathSelector("c:last-child").Matches(c), "c:last-child");
Assert.IsTrue(new XPathSelector("d:first-child").Matches(d), "d:first-child");
Assert.IsTrue(!(new XPathSelector("b:last-child").Matches(b)), "b:last-child");
}
[Test]
public void TestNthChildMatching()
{
Assert.IsTrue(new XPathSelector("*:nth-child(2)").Matches(e), "1: *:nth-child(2)");
Assert.IsTrue(new XPathSelector("e:nth-child(2n)").Matches(e), "e:nth-child(2n)");
Assert.IsTrue(new XPathSelector("e:nth-child(n)").Matches(e), "e:nth-child(n)");
Assert.IsTrue(new XPathSelector("e:nth-child(4n-2)").Matches(e), "e:nth-child(4n- 2)");
Assert.IsTrue(new XPathSelector("g:nth-child(2n)").Matches(g), "g:nth-child(2n)");
Assert.IsTrue(new XPathSelector("b:nth-child(0n + 1)").Matches(b), "b:nth-child( 0n + 1)");
Assert.IsTrue(!(new XPathSelector("e:nth-child( 0n+1)").Matches(e)), "e:nth-child( 0n+1)");
Assert.IsTrue(new XPathSelector("*:nth-child(-n+2)").Matches(b), "*:nth-child(-n+2)");
Assert.IsTrue(new XPathSelector("*:nth-child(-n+2)").Matches(e), "*:nth-child(-n+2)");
Assert.IsTrue(!(new XPathSelector("*:nth-child(-n+2)").Matches(f)), "*:nth-child(-n+2)");
Assert.IsTrue(!(new XPathSelector("*:nth-child(-n+2)").Matches(g)), "*:nth-child(-n+2)");
// test of odd and even
Assert.IsTrue(new XPathSelector("e:nth-child(even)").Matches(e), "e:nth-child(even)");
Assert.IsTrue(new XPathSelector("b:nth-child(odd)").Matches(b), "b:nth-child(odd)");
Assert.IsTrue(new XPathSelector("f:nth-child(odd)").Matches(f), "f:nth-child(odd)");
Assert.IsTrue(!(new XPathSelector("e:nth-child(odd)").Matches(e)), "e:nth-child(odd)");
Assert.IsTrue(!(new XPathSelector("b:nth-child(even)").Matches(b)), "b:nth-child(even)");
// tests for false positives
Assert.IsTrue(!(new XPathSelector("b:nth-child(2n)").Matches(b)), "b:nth-child(2n)");
Assert.IsTrue(!(new XPathSelector("f:nth-child(2n)").Matches(b)), "f:nth-child(2n)");
}
public void TestNthLastChildMatching()
{
Assert.IsTrue(new XPathSelector("*:nth-last-child(5)").Matches(e), "1: *:nth-last-child(5)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(-n+2)").Matches(g), "2: *:nth-last-child(-n+2)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(-n+2)").Matches(f), "3: *:nth-last-child(-n+2)");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(-n+2)").Matches(e)), "4: *:nth-last-child(-n+2)");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(-n+2)").Matches(b)), "5: *:nth-last-child(-n+2)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(odd)").Matches(g), "*:nth-last-child( odd)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(odd)").Matches(e), "*:nth-last-child(odd)");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(odd)").Matches(f)), "*:nth-last-child(odd )");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(odd)").Matches(b)), "*:nth-last-child(odd)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(even)").Matches(f), "*:nth-last-child(even)");
Assert.IsTrue(new XPathSelector("*:nth-last-child(even)").Matches(b), "*:nth-last-child( even)");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(even)").Matches(g)), "*:nth-last-child(even)");
Assert.IsTrue(!(new XPathSelector("*:nth-last-child(even)").Matches(e)), "*:nth-last-child(even )");
}
#endregion
private void PrintXPath(string sel)
{
this.PrintXPath(sel, new Hashtable());
}
private void PrintXPath(string sel, Hashtable nsTable)
{
XPathSelector xsel = new XPathSelector(sel, nsTable);
xsel.Matches(e);
// Console.WriteLine(xsel.XPath);
}
}
#endif
#endregion
}
}
|