css3-selectors
changeset 70:0c04c468e014
Autoconfiscated the project, organizing the sources in a more sane way
| author | Emanuele Aina <em@nerd.ocracy.org> |
|---|---|
| date | Tue Dec 30 22:26:25 2008 +0100 (19 months ago) |
| parents | fe189fede98d |
| children | 5d6a86f7f2b1 |
| files | AUTHORS COPYING ChangeLog Css3SelectorExperiment.cs LICENSE LxmlTestCssPy.cs Makefile Makefile.am NEWS README autogen.sh configure.ac css3-selectors.pc.in lxml/css_shakespear.html lxml/test_css.py lxml/test_css.txt lxml/test_css_select.txt selectors.txt src/AssemblyInfo.cs.in src/Css3Selectors.cs src/Makefile.am tests/LxmlTestCssPy.cs tests/Makefile.am tests/TestsRunner.cs tests/lxml/css_shakespear.html tests/lxml/test_css.py tests/lxml/test_css.txt tests/lxml/test_css_select.txt tests/selectors.txt |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/AUTHORS Tue Dec 30 22:26:25 2008 +0100 1.3 @@ -0,0 +1,1 @@ 1.4 +Emanuele Aina <em@nerd.ocracy.org>
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/COPYING Tue Dec 30 22:26:25 2008 +0100 2.3 @@ -0,0 +1,21 @@ 2.4 +Template: http://www.opensource.org/licenses/mit-license.html 2.5 + 2.6 +Copyright (c) 2008-2009 Emanuele Aina <em@nerd.ocracy.org> 2.7 + 2.8 +Permission is hereby granted, free of charge, to any person obtaining a copy 2.9 +of this software and associated documentation files (the "Software"), to deal 2.10 +in the Software without restriction, including without limitation the rights 2.11 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 2.12 +copies of the Software, and to permit persons to whom the Software is 2.13 +furnished to do so, subject to the following conditions: 2.14 + 2.15 +The above copyright notice and this permission notice shall be included in 2.16 +all copies or substantial portions of the Software. 2.17 + 2.18 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2.19 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2.20 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2.21 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2.22 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2.23 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2.24 +THE SOFTWARE.
3.1 --- a/Css3SelectorExperiment.cs Tue Dec 30 18:36:08 2008 +0100 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,363 +0,0 @@ 3.4 -/* CSS3 Selectors to XPath converter 3.5 - * 3.6 - * Emanuele Aina <em@nerd.ocracy.org> 3.7 - * 3.8 - * Inspired by HTML::Selector::XPath from Tatsuhiko Miyagawa 3.9 - * http://search.cpan.org/~miyagawa/HTML-Selector-XPath/lib/HTML/Selector/XPath.pm 3.10 - */ 3.11 -using System; 3.12 -using System.Collections.Generic; 3.13 -using System.Linq; 3.14 -using System.IO; 3.15 -using System.Text.RegularExpressions; 3.16 - 3.17 -public static class Css { 3.18 - public class Element { 3.19 - public virtual string ToCss() { 3.20 - return ""; 3.21 - } 3.22 - 3.23 - public virtual string ToXPath() { 3.24 - return ""; 3.25 - } 3.26 - public override string ToString() { 3.27 - return base.ToString() + "(" + ToCss() + ")"; 3.28 - } 3.29 - } 3.30 - 3.31 - public class Combinator : Element { 3.32 - public string Mode { get; set; } 3.33 - public override string ToCss() { 3.34 - return Mode; 3.35 - } 3.36 - 3.37 - public override string ToXPath() { 3.38 - switch(Mode) { 3.39 - case " ": return "//"; 3.40 - case ">": return "/"; 3.41 - case "+": return "/following-sibling::*[1]/self::"; 3.42 - case "~": return "/following-sibling::"; 3.43 - } 3.44 - throw new Exception(); 3.45 - } 3.46 - } 3.47 - 3.48 - public class Comma : Element { 3.49 - } 3.50 - 3.51 - public class SimpleSelector : Element { 3.52 - } 3.53 - 3.54 - public class TypeSelector : SimpleSelector { 3.55 - public string Namespace { get; set; } 3.56 - public string Type { get; set; } 3.57 - public override string ToCss() { 3.58 - if(Namespace == null) 3.59 - return Type; 3.60 - return Namespace + "|" + Type; 3.61 - } 3.62 - 3.63 - public override string ToXPath() { 3.64 - if(Namespace == null) 3.65 - return Type; 3.66 - return Namespace + ":" + Type; 3.67 - } 3.68 - } 3.69 - 3.70 - public class UniversalSelector : SimpleSelector { 3.71 - public override string ToCss() { 3.72 - return "*"; 3.73 - } 3.74 - 3.75 - public override string ToXPath() { 3.76 - return "*"; 3.77 - } 3.78 - } 3.79 - 3.80 - public class AttributeSelector : SimpleSelector { 3.81 - public string Attribute { get; set; } 3.82 - public string Comparison { get; set; } 3.83 - public string Value { get; set; } 3.84 - public override string ToCss() { 3.85 - if(Comparison == null) 3.86 - return "[" + Attribute + "]"; 3.87 - return "[" + Attribute + Comparison + "'" + Value + "']"; 3.88 - } 3.89 - 3.90 - public override string ToXPath() { 3.91 - switch(Comparison) { 3.92 - case null: return "[@" + Attribute + "]"; 3.93 - case "=" : return "[@" + Attribute + "='" + Value + "']"; 3.94 - case "|=": return String.Format("[@{0}='{1}' or starts-with(@{0},'{1}-')]", Attribute, Value); 3.95 - case "~=": return String.Format("[contains(concat(' ',@{0},' '),' {1} ')]", Attribute, Value); 3.96 - case "^=": return String.Format("[starts-with(@{0},'{1}')]", Attribute, Value); 3.97 - // TODO: add the ends-with() XPath function 3.98 - case "$=": return String.Format("[ends-with(@{0},'{1}')]", Attribute, Value); 3.99 - case "*=": return String.Format("[contains(@{0},'{1}')]", Attribute, Value); 3.100 - // NOTE: non-standard! 3.101 - case "!=": return String.Format("[not(@{0}='{1}')]", Attribute, Value); 3.102 - } 3.103 - throw new Exception(); 3.104 - } 3.105 - } 3.106 - 3.107 - public class ClassSelector : SimpleSelector { 3.108 - public string Class { get; set; } 3.109 - public override string ToCss() { 3.110 - return "." + Class; 3.111 - } 3.112 - 3.113 - public override string ToXPath() { 3.114 - return String.Format("[contains(concat(' ',@class,' '),' {0} ')]", Class); 3.115 - } 3.116 - } 3.117 - 3.118 - public class IdSelector : SimpleSelector { 3.119 - public string Id { get; set; } 3.120 - public override string ToCss() { 3.121 - return "#" + Id; 3.122 - } 3.123 - 3.124 - public override string ToXPath() { 3.125 - return String.Format("[@id='{0}']", Id); 3.126 - } 3.127 - } 3.128 - 3.129 - public class ContentSelector : SimpleSelector { 3.130 - // This public class intentionally left blank 3.131 - // See http://www.w3.org/TR/css3-selectors/#content-selectors 3.132 - } 3.133 - 3.134 - public class PseudoClassSelector : SimpleSelector { 3.135 - public string PseudoClass { get; set; } 3.136 - 3.137 - public override string ToCss() { 3.138 - return ":"+PseudoClass; 3.139 - } 3.140 - 3.141 - public override string ToXPath() { 3.142 - switch(PseudoClass) { 3.143 - case "only-child": return "[count(../*)=1]"; 3.144 - case "first-child": return "*[1]/self::"; 3.145 - case "last-child": return "*[last()]/self::"; 3.146 - } 3.147 - throw new Exception(); 3.148 - } 3.149 - } 3.150 - 3.151 - public class PseudoClassSelector<T> : PseudoClassSelector { 3.152 - public T Argument { get; set; } 3.153 - 3.154 - private string ToCssString(object obj) { 3.155 - SimpleSelector selector = obj as SimpleSelector; 3.156 - 3.157 - if(selector != null) return selector.ToCss(); 3.158 - IEnumerable<Element> tokens = obj as IEnumerable<Css.Element>; 3.159 - 3.160 - if(tokens != null) return tokens.Select(t => t.ToCss()).Aggregate((a,b) => a+b); 3.161 - return obj.ToString(); 3.162 - } 3.163 - 3.164 - private string ToXPathString(object obj) { 3.165 - SimpleSelector selector = obj as SimpleSelector; 3.166 - if(selector != null) return selector.ToXPath(); 3.167 - 3.168 - IEnumerable<Element> tokens = obj as IEnumerable<Css.Element>; 3.169 - if(tokens != null) return Regex.Replace(tokens.ToXPath(), @"^//", "./self::"); 3.170 - 3.171 - return obj.ToString(); 3.172 - } 3.173 - 3.174 - public override string ToCss() { 3.175 - return ":"+PseudoClass + "(" + ToCssString(Argument) + ")"; 3.176 - } 3.177 - 3.178 - public override string ToXPath() { 3.179 - switch(PseudoClass) { 3.180 - case "not": return String.Format("[not({0})]", ToXPathString(Argument)); 3.181 - case "nth-child": return String.Format("[count(preceding-sibling::*)={0}-1]", Argument); 3.182 - case "lang": return String.Format("[@xml:lang='{0}' or starts-with(@xml:lang,'{0}-')]", Argument); 3.183 - // TODO: removed from the CSS3 draft, underspecified, check its behaviour against other implementations 3.184 - case "contains": return String.Format("[contains(text(),'{0}')]", Argument); 3.185 - } 3.186 - throw new Exception(); 3.187 - } 3.188 - } 3.189 - 3.190 - public class PseudoElement { 3.191 - } 3.192 - 3.193 - private static string Unquote(string s) { 3.194 - Regex singleQuotes = new Regex("'(.*)'"); 3.195 - Regex doubleQuotes = new Regex("\"(.*)\""); 3.196 - 3.197 - if(singleQuotes.Match(s).Success) 3.198 - return singleQuotes.Replace(s, "$1"); 3.199 - if(doubleQuotes.Match(s).Success) 3.200 - return doubleQuotes.Replace(s, "$1"); 3.201 - return s; 3.202 - } 3.203 - 3.204 - public static IEnumerable<Element> Parse(string selector) 3.205 - { 3.206 - string tmp = selector; 3.207 - 3.208 - var parseTable = new[] { 3.209 - // tag name/id/class 3.210 - new[]{@"^([#.]?)([a-z0-9\\*_-]+)((\|)([a-z0-9\\*_-]*))?", "Element"}, 3.211 - // pseudo classes 3.212 - new[]{@"^:([a-z0-9_-]+)(\((.*)\))?", "Pseudo" }, 3.213 - // attribute value match 3.214 - new[]{@"^\[\s*([^\~\|^$*!=\s]+)\s*(([~\|^$*!]?=)\s*(.+))?\]", "Attr"}, 3.215 - // adjacency/direct descendance 3.216 - new[]{@"^\s*([>+\~\s])\s*", "Combinator"}, 3.217 - // rules union 3.218 - new[]{@"^\s*,", "Comma"} 3.219 - }; 3.220 - 3.221 - while(!String.IsNullOrEmpty(tmp)) { 3.222 - Match match = null; 3.223 - string type = null; 3.224 - 3.225 - Console.WriteLine(" rest -> '{0}'", tmp); 3.226 - 3.227 - foreach(var item in parseTable) { 3.228 - Regex r = new Regex(item[0], RegexOptions.Compiled | RegexOptions.IgnoreCase); 3.229 - match = r.Match(tmp); 3.230 - if(match.Success) { 3.231 - type = item[1]; 3.232 - tmp = r.Replace(tmp, ""); 3.233 - break; 3.234 - } 3.235 - } 3.236 - 3.237 - Console.WriteLine(" {0} -> '{1}'", type, match.Value); 3.238 - switch(type) { 3.239 - case "Element": 3.240 - switch(match.Groups[1].Value) { 3.241 - case "": 3.242 - if(match.Groups[5].Success) { 3.243 - yield return new TypeSelector{Namespace = match.Groups[2].Value, Type = match.Groups[5].Value}; 3.244 - } 3.245 - else { 3.246 - yield return new TypeSelector{Type = match.Groups[2].Value}; 3.247 - } 3.248 - break; 3.249 - case "#": 3.250 - yield return new IdSelector{Id = match.Groups[2].Value}; 3.251 - break; 3.252 - case ".": 3.253 - yield return new ClassSelector{Class = match.Groups[2].Value}; 3.254 - break; 3.255 - } 3.256 - break; 3.257 - case "Attr": 3.258 - if(match.Groups[3].Success) { 3.259 - yield return new AttributeSelector{ 3.260 - Attribute = Unquote(match.Groups[1].Value.Trim()), 3.261 - Comparison = match.Groups[3].Value, 3.262 - Value = Unquote(match.Groups[4].Value)}; 3.263 - } 3.264 - else { 3.265 - yield return new AttributeSelector{Attribute = match.Groups[1].Value}; 3.266 - } 3.267 - break; 3.268 - case "Pseudo": 3.269 - if(match.Groups[3].Success) 3.270 - switch(match.Groups[1].Value) { 3.271 - case "not": 3.272 - IEnumerable<Element> negated = Parse(match.Groups[3].Value); 3.273 - yield return new PseudoClassSelector<IEnumerable<Element>> 3.274 - {PseudoClass = match.Groups[1].Value, Argument = negated}; 3.275 - break; 3.276 - default: 3.277 - yield return new PseudoClassSelector<string>{PseudoClass = match.Groups[1].Value, Argument = match.Groups[3].Value}; 3.278 - break; 3.279 - } 3.280 - else 3.281 - yield return new PseudoClassSelector{PseudoClass = match.Groups[1].Value }; 3.282 - break; 3.283 - case "Combinator": 3.284 - yield return new Combinator{Mode = match.Groups[1].Value}; 3.285 - break; 3.286 - case "Comma": 3.287 - yield return new Comma(); 3.288 - break; 3.289 - default: 3.290 - throw new ArgumentException(String.Format("invalid selector: {0}", selector)); 3.291 - } 3.292 - } 3.293 - } 3.294 - 3.295 - public static string ToXPath(this IEnumerable<Element> tokens) { 3.296 - var parts = new [] {"//", "*"}.ToList(); 3.297 - var index = 1; 3.298 - 3.299 - foreach(Element token in tokens) { 3.300 - if (token is SimpleSelector) { 3.301 - SimpleSelector selector = (SimpleSelector)token; 3.302 - 3.303 - if (selector is TypeSelector) { 3.304 - parts[index] = selector.ToXPath(); 3.305 - } 3.306 - else if (selector is PseudoClassSelector) { 3.307 - PseudoClassSelector pseudo = (PseudoClassSelector)selector; 3.308 - switch(pseudo.PseudoClass) { 3.309 - case "first-child": 3.310 - case "last-child": 3.311 - int last = parts.Count - 1; 3.312 - parts[last] = pseudo.ToXPath() + parts[last]; 3.313 - break; 3.314 - default: 3.315 - parts.Add(pseudo.ToXPath()); 3.316 - break; 3.317 - } 3.318 - } 3.319 - else { 3.320 - parts.Add(selector.ToXPath()); 3.321 - } 3.322 - } 3.323 - else if (token is Combinator) { 3.324 - Combinator combinator = (Combinator)token; 3.325 - parts.Add(combinator.ToXPath()); 3.326 - 3.327 - index = parts.Count; 3.328 - parts.Add("*"); 3.329 - } 3.330 - else if (token is Comma) { 3.331 - parts.Add(" | "); 3.332 - } 3.333 - else { 3.334 - throw new Exception(); 3.335 - } 3.336 - } 3.337 - 3.338 - return String.Join("", parts.ToArray()); 3.339 - } 3.340 -} 3.341 - 3.342 -class Css3SelectorExperiment { 3.343 - public static void Main (string[] args) 3.344 - { 3.345 - using(TextReader selectors = new StreamReader("selectors.txt")) { 3.346 - string line; 3.347 - while ((line = selectors.ReadLine()) != null) { 3.348 - string selector, reference, xpath; 3.349 - var split = line.Split(new[]{','}, 2); 3.350 - selector = split[0]; 3.351 - reference = split[1].Trim(); 3.352 - 3.353 - Console.WriteLine("CSS: {0}", selector); 3.354 - var tokens = Css.Parse(selector).ToList(); 3.355 - Console.WriteLine(" tokens -> {0}", tokens.Select(t => t.ToString()).Aggregate((a,b) => a+" "+b)); 3.356 - xpath = tokens.ToXPath(); 3.357 - if(xpath == reference) 3.358 - Console.WriteLine("OK '{0}' -> '{1}'", selector, reference); 3.359 - else 3.360 - Console.WriteLine("ERROR on '{0}' -> '{1}' (expected '{2}')", selector, xpath, reference); 3.361 - } 3.362 - } 3.363 - 3.364 - LxmlTestCssPy.Run(); 3.365 - } 3.366 -}
4.1 --- a/LICENSE Tue Dec 30 18:36:08 2008 +0100 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,21 +0,0 @@ 4.4 -Template: http://www.opensource.org/licenses/mit-license.html 4.5 - 4.6 -Copyright (c) 2008 Emanuele Aina <em@nerd.ocracy.org> 4.7 - 4.8 -Permission is hereby granted, free of charge, to any person obtaining a copy 4.9 -of this software and associated documentation files (the "Software"), to deal 4.10 -in the Software without restriction, including without limitation the rights 4.11 -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 4.12 -copies of the Software, and to permit persons to whom the Software is 4.13 -furnished to do so, subject to the following conditions: 4.14 - 4.15 -The above copyright notice and this permission notice shall be included in 4.16 -all copies or substantial portions of the Software. 4.17 - 4.18 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4.19 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4.20 -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4.21 -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 4.22 -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 4.23 -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 4.24 -THE SOFTWARE.
5.1 --- a/LxmlTestCssPy.cs Tue Dec 30 18:36:08 2008 +0100 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,75 +0,0 @@ 5.4 -/* Tests for the CSS3 Selectors to XPath converter 5.5 - * 5.6 - * Parse the tests in the test_css.py python file from lxml and run them 5.7 - * 5.8 - * Emanuele Aina <em@nerd.ocracy.org> 5.9 - */ 5.10 -using System; 5.11 -using System.Xml; 5.12 -using System.Collections.Generic; 5.13 -using System.Linq; 5.14 -using System.IO; 5.15 -using System.Text.RegularExpressions; 5.16 - 5.17 -public static class LxmlTestCssPy { 5.18 - public static void Run() 5.19 - { 5.20 - XmlDocument doc = new XmlDocument(); 5.21 - doc.Load("lxml/css_shakespear.html"); 5.22 - 5.23 - XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable); 5.24 - ns.AddNamespace("_", "http://www.w3.org/1999/xhtml"); 5.25 - 5.26 - // Remove the <head> tag, as it is not counted when matching in html 5.27 - XmlNode head = doc.SelectSingleNode("//_:head", ns); 5.28 - Console.WriteLine("{0} {1} {2}", head, ns.DefaultNamespace, doc.NameTable.Get(String.Empty)); 5.29 - head.ParentNode.RemoveChild(head); 5.30 - 5.31 - // Then match selectors starting from the <html> tag 5.32 - XmlNode html = doc.SelectSingleNode("//_:html", ns); 5.33 - 5.34 - using(TextReader py = new StreamReader("lxml/test_css.py")) { 5.35 - string line; 5.36 - while ((line = py.ReadLine()) != null) { 5.37 - if(line.Trim().StartsWith("selectors")) 5.38 - break; 5.39 - } 5.40 - while ((line = py.ReadLine()) != null) { 5.41 - if(line.Trim().StartsWith("#")) 5.42 - continue; 5.43 - if(line.Trim().StartsWith("]")) 5.44 - break; 5.45 - Match m = Regex.Match(line, @"\('(.*)', (\d+)\)"); 5.46 - if(!m.Success) 5.47 - throw new Exception(); 5.48 - string selector = m.Groups[1].Value; 5.49 - int reference = Int32.Parse(m.Groups[2].Value); 5.50 - 5.51 - Console.WriteLine("selector, reference: {0}, {1}", selector, reference); 5.52 - 5.53 - int count = -1; 5.54 - string xpath = null; 5.55 - Exception exception = null; 5.56 - try { 5.57 - xpath = Css.Parse(selector).Select(s => { 5.58 - if (s is Css.TypeSelector) 5.59 - ((Css.TypeSelector)s).Namespace = "_"; 5.60 - return s; 5.61 - }).ToXPath(); 5.62 - // Prefix the xpath expression with the current node, otherwise the <html> tag 5.63 - // will be matched too 5.64 - count = html.SelectNodes("."+xpath, ns).Count; 5.65 - } catch (Exception e){ 5.66 - exception = e; 5.67 - } 5.68 - 5.69 - if(count == reference) 5.70 - Console.WriteLine("OK '{0}' -> '{1}' ({2})", selector, xpath, count); 5.71 - else if(exception != null) 5.72 - Console.WriteLine("ERROR on '{0}' -> '{1}' (got {2}, expected '{3}', {4})", selector, xpath, count, reference, exception); 5.73 - else 5.74 - Console.WriteLine("ERROR on '{0}' -> '{1}' (got {2}, expected '{3}')", selector, xpath, count, reference); 5.75 - } 5.76 - } 5.77 - } 5.78 -}
6.1 --- a/Makefile Tue Dec 30 18:36:08 2008 +0100 6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 6.3 @@ -1,17 +0,0 @@ 6.4 -SOURCES := Css3SelectorExperiment.cs LxmlTestCssPy.cs 6.5 -EXE := Css3SelectorExperiment.exe 6.6 -REFERENCES := $(addprefix -r:, System.Core) 6.7 - 6.8 -MCS := gmcs 6.9 -MONO := mono --debug 6.10 - 6.11 -all: $(EXE) 6.12 - 6.13 -run: $(EXE) 6.14 - @${MONO} $(EXE) 6.15 - 6.16 -clean: 6.17 - -rm -Rf $(EXE) 6.18 - 6.19 -$(EXE): $(SOURCES) 6.20 - $(MCS) $(REFERENCES) -out:"$@" $^
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/Makefile.am Tue Dec 30 22:26:25 2008 +0100 7.3 @@ -0,0 +1,12 @@ 7.4 +SUBDIRS = src tests 7.5 +EXTRA_DIST = css3-selectors.pc.in 7.6 + 7.7 +pkgconfigdir = $(libdir)/pkgconfig 7.8 +pkgconfig_DATA = css3-selectors.pc 7.9 + 7.10 +DISTCLEANFILES = config.guess config.sub Makefile.in configure COPYING INSTALL install-sh missing aclocal.m4 7.11 + 7.12 + 7.13 +MONO := mono --debug 7.14 +run: all 7.15 + @MONO_PATH=src/ ${MONO} tests/testsrunner.exe
8.1 --- a/README Tue Dec 30 18:36:08 2008 +0100 8.2 +++ b/README Tue Dec 30 22:26:25 2008 +0100 8.3 @@ -1,18 +1,22 @@ 8.4 -CSS3 to XPath converter 8.5 -======================= 8.6 +CSS3 Selectors 8.7 +============== 8.8 8.9 -This is an incomplete converter from CSS3 selectors to XPath 8.10 -expressions written in C# using Mono. 8.11 +This is a .NET library to convert extendend CSS Level 3 selectors to XPath 8.12 +expression written in C#. 8.13 +This enable the use of CSS selectors to select nodes in the XML DOM, in 8.14 +XML to Linq trees or with any interface that supports XPath expressions. 8.15 8.16 -It parses CSS3 selectors to a list of elements and then it translates 8.17 -this list to the corresponding XPath expression. 8.18 +The code is released under a MIT/X11-style license (see COPYING). 8.19 8.20 -It currently handles type, id (#foo), and class (.foo) selectors, 8.21 -attribute selectors with various comparison methods (e.g. 8.22 -[foo~='bar'], [foo]), some pseudo-classes (e.g. :first-child, 8.23 -:nth-child, :lang, :not), and combinators (e.g. '+', '>'). 8.24 +Development 8.25 +=========== 8.26 8.27 -It should be fairly easy to extend as the parsing and tranlsation 8.28 -phases are clearly separated. 8.29 +The code has been developed and tested with Mono, but should work even 8.30 +with other .NET implementation. 8.31 8.32 -The code is released under a MIT/X11 style license (see LICENSE). 8.33 +The source code is available in a Mercurial repository at: 8.34 + 8.35 +http://techn.ocracy.org/css3-selectors 8.36 + 8.37 +Feel free to ask anything about this library to 8.38 +Emanuele Aina <em@nerd.ocracy.org>
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/autogen.sh Tue Dec 30 22:26:25 2008 +0100 9.3 @@ -0,0 +1,84 @@ 9.4 +#! /bin/sh 9.5 + 9.6 +PROJECT=css3-selector 9.7 +FILE=src/Css3Selectors.cs 9.8 +CONFIGURE=configure.ac 9.9 + 9.10 +: ${AUTOCONF=autoconf} 9.11 +: ${AUTOHEADER=autoheader} 9.12 +: ${AUTOMAKE=automake} 9.13 +: ${LIBTOOLIZE=libtoolize} 9.14 +: ${ACLOCAL=aclocal} 9.15 +: ${LIBTOOL=libtool} 9.16 + 9.17 +srcdir=`dirname $0` 9.18 +test -z "$srcdir" && srcdir=. 9.19 + 9.20 +ORIGDIR=`pwd` 9.21 +cd $srcdir 9.22 +TEST_TYPE=-f 9.23 +aclocalinclude="-I . $ACLOCAL_FLAGS" 9.24 + 9.25 +DIE=0 9.26 + 9.27 +($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || { 9.28 + echo 9.29 + echo "You must have autoconf installed to compile $PROJECT." 9.30 + echo "Download the appropriate package for your distribution," 9.31 + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" 9.32 + DIE=1 9.33 +} 9.34 + 9.35 +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { 9.36 + echo 9.37 + echo "You must have automake installed to compile $PROJECT." 9.38 + echo "Get ftp://sourceware.cygnus.com/pub/automake/automake-1.4.tar.gz" 9.39 + echo "(or a newer version if it is available)" 9.40 + DIE=1 9.41 +} 9.42 + 9.43 +(grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null) && { 9.44 + ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || { 9.45 + echo 9.46 + echo "**Error**: You must have \`libtool' installed to compile $PROJECT." 9.47 + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" 9.48 + echo "(or a newer version if it is available)" 9.49 + DIE=1 9.50 + } 9.51 +} 9.52 + 9.53 +if test "$DIE" -eq 1; then 9.54 + exit 1 9.55 +fi 9.56 + 9.57 +test $TEST_TYPE $FILE || { 9.58 + echo "You must run this script in the top-level $PROJECT directory" 9.59 + exit 1 9.60 +} 9.61 + 9.62 +if test -z "$*"; then 9.63 + echo "I am going to run ./configure with no arguments - if you wish " 9.64 + echo "to pass any to it, please specify them on the $0 command line." 9.65 +fi 9.66 + 9.67 +case $CC in 9.68 +*xlc | *xlc\ * | *lcc | *lcc\ *) am_opt=--include-deps;; 9.69 +esac 9.70 + 9.71 +(grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null) && { 9.72 + echo "Running $LIBTOOLIZE ..." 9.73 + $LIBTOOLIZE --force --copy 9.74 +} 9.75 + 9.76 +echo "Running $ACLOCAL $aclocalinclude ..." 9.77 +$ACLOCAL $aclocalinclude 9.78 + 9.79 +echo "Running $AUTOMAKE --gnu $am_opt ..." 9.80 +$AUTOMAKE --add-missing --gnu $am_opt 9.81 + 9.82 +echo "Running $AUTOCONF ..." 9.83 +$AUTOCONF 9.84 + 9.85 +echo Running $srcdir/configure $conf_flags "$@" ... 9.86 +$srcdir/configure --enable-maintainer-mode $conf_flags "$@" \ 9.87 +
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/configure.ac Tue Dec 30 22:26:25 2008 +0100 10.3 @@ -0,0 +1,39 @@ 10.4 +AC_INIT(README) 10.5 +AC_CANONICAL_SYSTEM 10.6 +AM_INIT_AUTOMAKE(css3-selectors, 0.1) 10.7 +AM_MAINTAINER_MODE 10.8 + 10.9 +AC_PROG_INSTALL 10.10 + 10.11 +dnl pkg-config 10.12 +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) 10.13 +if test "x$PKG_CONFIG" = "xno"; then 10.14 + AC_MSG_ERROR([You need to install pkg-config]) 10.15 +fi 10.16 + 10.17 +AC_PATH_PROG(CSC, csc, no) 10.18 +AC_PATH_PROG(MCS, gmcs, no) 10.19 +AC_PATH_PROG(MONO, mono, no) 10.20 + 10.21 +CS="C#" 10.22 +if test "x$CSC" = "xno" -a "x$MCS" = "xno" ; then 10.23 + AC_MSG_ERROR([No $CS compiler found]) 10.24 +fi 10.25 + 10.26 +if test "x$MCS" = "xno" ; then 10.27 + MCS=$CSC 10.28 +fi 10.29 + 10.30 +if test "x$MONO" = "xno"; then 10.31 + AC_MSG_ERROR([No mono runtime found]) 10.32 +fi 10.33 + 10.34 +AC_SUBST(MCS) 10.35 + 10.36 +AC_OUTPUT([ 10.37 +Makefile 10.38 +css3-selectors.pc 10.39 +src/Makefile 10.40 +tests/Makefile 10.41 +src/AssemblyInfo.cs 10.42 +])
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/css3-selectors.pc.in Tue Dec 30 22:26:25 2008 +0100 11.3 @@ -0,0 +1,11 @@ 11.4 +prefix=@prefix@ 11.5 +exec_prefix=@exec_prefix@ 11.6 +libdir=@libdir@ 11.7 +includedir=@includedir@ 11.8 +Libraries=@libdir@/css3-selectors/css3-selectors.dll 11.9 + 11.10 +Name: css3-selectors 11.11 +Description: css3-selectors - CSS Level 3 Selectors to XPath converter 11.12 +Version: @VERSION@ 11.13 +Libs: -r:@libdir@/css3-selectors/css3-selectors.dll 11.14 +
12.1 --- a/lxml/css_shakespear.html Tue Dec 30 18:36:08 2008 +0100 12.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 12.3 @@ -1,526 +0,0 @@ 12.4 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 12.5 - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 12.6 - 12.7 -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" debug="true"> 12.8 -<head> 12.9 - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 12.10 -</head> 12.11 - 12.12 -<body> 12.13 - 12.14 - <div id="test"> 12.15 - <div class="dialog"> 12.16 - <h2>As You Like It</h2> 12.17 - <div id="playwright"> 12.18 - 12.19 - by William Shakespeare 12.20 - 12.21 - 12.22 - </div> 12.23 - <div class="dialog scene thirdClass" id="scene1"> 12.24 - 12.25 - <h3>ACT I, SCENE III. A room in the palace.</h3> 12.26 - 12.27 - <div class="dialog"> 12.28 - <div class="direction">Enter CELIA and ROSALIND</div> 12.29 - 12.30 - </div> 12.31 - 12.32 - <div id="speech1" class="character">CELIA</div> 12.33 - 12.34 - <div class="dialog"> 12.35 - <div id="scene1.3.1">Why, cousin! why, Rosalind! Cupid have mercy! not a word?</div> 12.36 - 12.37 - </div> 12.38 - 12.39 - <div id="speech2" class="character">ROSALIND</div> 12.40 - 12.41 - <div class="dialog"> 12.42 - <div id="scene1.3.2">Not one to throw at a dog.</div> 12.43 - 12.44 - </div> 12.45 - 12.46 - <div id="speech3" class="character">CELIA</div> 12.47 - 12.48 - <div class="dialog"> 12.49 - <div id="scene1.3.3">No, thy words are too precious to be cast away upon</div> 12.50 - 12.51 - <div id="scene1.3.4">curs; throw some of them at me; come, lame me with reasons.</div> 12.52 - 12.53 - </div> 12.54 - 12.55 - <div id="speech4" class="character">ROSALIND</div> 12.56 - 12.57 - <div id="speech5" class="character">CELIA</div> 12.58 - 12.59 - <div class="dialog"> 12.60 - <div id="scene1.3.8">But is all this for your father?</div> 12.61 - 12.62 - </div> 12.63 - 12.64 - <div class="dialog"> 12.65 - <div id="scene1.3.5">Then there were two cousins laid up; when the one</div> 12.66 - <div id="scene1.3.6">should be lamed with reasons and the other mad</div> 12.67 - 12.68 - <div id="scene1.3.7">without any.</div> 12.69 - </div> 12.70 - 12.71 - <div id="speech6" class="character">ROSALIND</div> 12.72 - 12.73 - <div class="dialog"> 12.74 - <div id="scene1.3.9">No, some of it is for my child's father. O, how</div> 12.75 - 12.76 - <div id="scene1.3.10">full of briers is this working-day world!</div> 12.77 - 12.78 - </div> 12.79 - 12.80 - <div id="speech7" class="character">CELIA</div> 12.81 - 12.82 - <div class="dialog"> 12.83 - 12.84 - <div id="scene1.3.11">They are but burs, cousin, thrown upon thee in</div> 12.85 - <div id="scene1.3.12">holiday foolery: if we walk not in the trodden</div> 12.86 - 12.87 - <div id="scene1.3.13">paths our very petticoats will catch them.</div> 12.88 - 12.89 - </div> 12.90 - 12.91 - <div id="speech8" class="character">ROSALIND</div> 12.92 - 12.93 - <div class="dialog"> 12.94 - <div id="scene1.3.14">I could shake them off my coat: these burs are in my heart.</div> 12.95 - </div> 12.96 - 12.97 - <div id="speech9" class="character">CELIA</div> 12.98 - 12.99 - <div class="dialog"> 12.100 - <div id="scene1.3.15">Hem them away.</div> 12.101 - 12.102 - </div> 12.103 - 12.104 - <div id="speech10" class="character">ROSALIND</div> 12.105 - 12.106 - <div class="dialog"> 12.107 - 12.108 - <div id="scene1.3.16">I would try, if I could cry 'hem' and have him.</div> 12.109 - </div> 12.110 - 12.111 - <div id="speech11" class="character">CELIA</div> 12.112 - 12.113 - <div class="dialog"> 12.114 - <div id="scene1.3.17">Come, come, wrestle with thy affections.</div> 12.115 - 12.116 - </div> 12.117 - 12.118 - <div id="speech12" class="character">ROSALIND</div> 12.119 - <div class="dialog"> 12.120 - <div id="scene1.3.18">O, they take the part of a better wrestler than myself!</div> 12.121 - 12.122 - </div> 12.123 - 12.124 - <div id="speech13" class="character">CELIA</div> 12.125 - 12.126 - <div class="dialog"> 12.127 - 12.128 - <div id="scene1.3.19">O, a good wish upon you! you will try in time, in</div> 12.129 - <div id="scene1.3.20">despite of a fall. But, turning these jests out of</div> 12.130 - <div id="scene1.3.21">service, let us talk in good earnest: is it</div> 12.131 - 12.132 - <div id="scene1.3.22">possible, on such a sudden, you should fall into so</div> 12.133 - 12.134 - <div id="scene1.3.23">strong a liking with old Sir Rowland's youngest son?</div> 12.135 - 12.136 - </div> 12.137 - 12.138 - <div id="speech14" class="character">ROSALIND</div> 12.139 - <div class="dialog"> 12.140 - <div id="scene1.3.24">The duke my father loved his father dearly.</div> 12.141 - 12.142 - </div> 12.143 - 12.144 - <div id="speech15" class="character">CELIA</div> 12.145 - 12.146 - <div class="dialog"> 12.147 - 12.148 - <div id="scene1.3.25">Doth it therefore ensue that you should love his son</div> 12.149 - 12.150 - <div id="scene1.3.26">dearly? By this kind of chase, I should hate him,</div> 12.151 - 12.152 - <div id="scene1.3.27">for my father hated his father dearly; yet I hate</div> 12.153 - 12.154 - <div id="scene1.3.28">not Orlando.</div> 12.155 - 12.156 - </div> 12.157 - 12.158 - <div id="speech16" class="character">ROSALIND</div> 12.159 - 12.160 - <div title="wtf" class="dialog"> 12.161 - 12.162 - <div id="scene1.3.29">No, faith, hate him not, for my sake.</div> 12.163 - 12.164 - </div> 12.165 - 12.166 - <div id="speech17" class="character">CELIA</div> 12.167 - 12.168 - <div class="dialog"> 12.169 - <div id="scene1.3.30">Why should I not? doth he not deserve well?</div> 12.170 - 12.171 - </div> 12.172 - 12.173 - <div id="speech18" class="character">ROSALIND</div> 12.174 - 12.175 - <div class="dialog"> 12.176 - <div id="scene1.3.31">Let me love him for that, and do you love him</div> 12.177 - <div id="scene1.3.32">because I do. Look, here comes the duke.</div> 12.178 - </div> 12.179 - 12.180 - <div id="speech19" class="character">CELIA</div> 12.181 - 12.182 - <div class="dialog"> 12.183 - 12.184 - <div id="scene1.3.33">With his eyes full of anger.</div> 12.185 - <div class="direction">Enter DUKE FREDERICK, with Lords</div> 12.186 - </div> 12.187 - 12.188 - <div id="speech20" class="character">DUKE FREDERICK</div> 12.189 - 12.190 - <div class="dialog"> 12.191 - 12.192 - <div id="scene1.3.34">Mistress, dispatch you with your safest haste</div> 12.193 - 12.194 - <div id="scene1.3.35">And get you from our court.</div> 12.195 - </div> 12.196 - 12.197 - <div id="speech21" class="character">ROSALIND</div> 12.198 - 12.199 - <div class="dialog"> 12.200 - 12.201 - <div id="scene1.3.36">Me, uncle?</div> 12.202 - 12.203 - </div> 12.204 - 12.205 - <div id="speech22" class="character">DUKE FREDERICK</div> 12.206 - <div class="dialog"> 12.207 - <div id="scene1.3.37">You, cousin</div> 12.208 - 12.209 - <div id="scene1.3.38">Within these ten days if that thou be'st found</div> 12.210 - 12.211 - <div id="scene1.3.39">So near our public court as twenty miles,</div> 12.212 - 12.213 - <div id="scene1.3.40">Thou diest for it.</div> 12.214 - 12.215 - </div> 12.216 - 12.217 - <div id="speech23" class="character">ROSALIND</div> 12.218 - 12.219 - <div class="dialog"> 12.220 - 12.221 - <div id="scene1.3.41"> I do beseech your grace,</div> 12.222 - 12.223 - <div id="scene1.3.42">Let me the knowledge of my fault bear with me:</div> 12.224 - <div id="scene1.3.43">If with myself I hold intelligence</div> 12.225 - 12.226 - <div id="scene1.3.44">Or have acquaintance with mine own desires,</div> 12.227 - 12.228 - <div id="scene1.3.45">If that I do not dream or be not frantic,--</div> 12.229 - 12.230 - <div id="scene1.3.46">As I do trust I am not--then, dear uncle,</div> 12.231 - 12.232 - <div id="scene1.3.47">Never so much as in a thought unborn</div> 12.233 - 12.234 - <div id="scene1.3.48">Did I offend your highness.</div> 12.235 - 12.236 - </div> 12.237 - 12.238 - <div id="speech24" class="character">DUKE FREDERICK</div> 12.239 - 12.240 - <div class="dialog"> 12.241 - <div id="scene1.3.49">Thus do all traitors:</div> 12.242 - 12.243 - <div id="scene1.3.50">If their purgation did consist in words,</div> 12.244 - 12.245 - <div id="scene1.3.51">They are as innocent as grace itself:</div> 12.246 - 12.247 - <div id="scene1.3.52">Let it suffice thee that I trust thee not.</div> 12.248 - 12.249 - </div> 12.250 - 12.251 - <div id="speech25" class="character">ROSALIND</div> 12.252 - 12.253 - <div class="dialog"> 12.254 - 12.255 - <div id="scene1.3.53">Yet your mistrust cannot make me a traitor:</div> 12.256 - 12.257 - <div id="scene1.3.54">Tell me whereon the likelihood depends.</div> 12.258 - 12.259 - </div> 12.260 - 12.261 - <div id="speech26" class="character">DUKE FREDERICK</div> 12.262 - <div class="dialog"> 12.263 - 12.264 - <div id="scene1.3.55">Thou art thy father's daughter; there's enough.</div> 12.265 - 12.266 - </div> 12.267 - 12.268 - <div id="speech27" class="character">ROSALIND</div> 12.269 - 12.270 - <div class="dialog"> 12.271 - <div id="scene1.3.56">So was I when your highness took his dukedom;</div> 12.272 - <div id="scene1.3.57">So was I when your highness banish'd him:</div> 12.273 - 12.274 - <div id="scene1.3.58">Treason is not inherited, my lord;</div> 12.275 - 12.276 - <div id="scene1.3.59">Or, if we did derive it from our friends,</div> 12.277 - 12.278 - <div id="scene1.3.60">What's that to me? my father was no traitor:</div> 12.279 - 12.280 - <div id="scene1.3.61">Then, good my liege, mistake me not so much</div> 12.281 - <div id="scene1.3.62">To think my poverty is treacherous.</div> 12.282 - 12.283 - </div> 12.284 - 12.285 - <div id="speech28" class="character">CELIA</div> 12.286 - <div class="dialog"> 12.287 - 12.288 - <div id="scene1.3.63">Dear sovereign, hear me speak.</div> 12.289 - 12.290 - </div> 12.291 - 12.292 - <div id="speech29" class="character">DUKE FREDERICK</div> 12.293 - 12.294 - <div class="dialog"> 12.295 - <div id="scene1.3.64">Ay, Celia; we stay'd her for your sake,</div> 12.296 - <div id="scene1.3.65">Else had she with her father ranged along.</div> 12.297 - 12.298 - </div> 12.299 - 12.300 - <div id="speech30" class="character">CELIA</div> 12.301 - 12.302 - <div class="dialog"> 12.303 - 12.304 - <div id="scene1.3.66">I did not then entreat to have her stay;</div> 12.305 - <div id="scene1.3.67">It was your pleasure and your own remorse:</div> 12.306 - <div id="scene1.3.68">I was too young that time to value her;</div> 12.307 - 12.308 - <div id="scene1.3.69">But now I know her: if she be a traitor,</div> 12.309 - 12.310 - <div id="scene1.3.70">Why so am I; we still have slept together,</div> 12.311 - 12.312 - <div id="scene1.3.71">Rose at an instant, learn'd, play'd, eat together,</div> 12.313 - <div id="scene1.3.72">And wheresoever we went, like Juno's swans,</div> 12.314 - 12.315 - <div id="scene1.3.73">Still we went coupled and inseparable.</div> 12.316 - </div> 12.317 - 12.318 - <div id="speech31" class="character">DUKE FREDERICK</div> 12.319 - 12.320 - <div class="dialog"> 12.321 - <div id="scene1.3.74">She is too subtle for thee; and her smoothness,</div> 12.322 - <div id="scene1.3.75">Her very silence and her patience</div> 12.323 - <div id="scene1.3.76">Speak to the people, and they pity her.</div> 12.324 - <div id="scene1.3.77">Thou art a fool: she robs thee of thy name;</div> 12.325 - 12.326 - <div id="scene1.3.78">And thou wilt show more bright and seem more virtuous</div> 12.327 - 12.328 - <div id="scene1.3.79">When she is gone. Then open not thy lips:</div> 12.329 - <div id="scene1.3.80">Firm and irrevocable is my doom</div> 12.330 - <div id="scene1.3.81">Which I have pass'd upon her; she is banish'd.</div> 12.331 - </div> 12.332 - 12.333 - <div id="speech32" class="character">CELIA</div> 12.334 - 12.335 - <div class="dialog"> 12.336 - <div id="scene1.3.82">Pronounce that sentence then on me, my liege:</div> 12.337 - <div id="scene1.3.83">I cannot live out of her company.</div> 12.338 - </div> 12.339 - 12.340 - <div id="speech33" class="character">DUKE FREDERICK</div> 12.341 - 12.342 - <div class="dialog"> 12.343 - <div id="scene1.3.84">You are a fool. You, niece, provide yourself:</div> 12.344 - 12.345 - <div id="scene1.3.85">If you outstay the time, upon mine honour,</div> 12.346 - <div id="scene1.3.86">And in the greatness of my word, you die.</div> 12.347 - <div class="direction">Exeunt DUKE FREDERICK and Lords</div> 12.348 - </div> 12.349 - 12.350 - <div id="speech34" class="character">CELIA</div> 12.351 - <div class="dialog"> 12.352 - 12.353 - <div id="scene1.3.87">O my poor Rosalind, whither wilt thou go?</div> 12.354 - 12.355 - <div id="scene1.3.88">Wilt thou change fathers? I will give thee mine.</div> 12.356 - <div id="scene1.3.89">I charge thee, be not thou more grieved than I am.</div> 12.357 - 12.358 - </div> 12.359 - 12.360 - <div id="speech35" class="character">ROSALIND</div> 12.361 - 12.362 - <div class="dialog"> 12.363 - 12.364 - <div id="scene1.3.90">I have more cause.</div> 12.365 - </div> 12.366 - 12.367 - <div id="speech36" class="character">CELIA</div> 12.368 - 12.369 - <div class="dialog"> 12.370 - <div id="scene1.3.91"> Thou hast not, cousin;</div> 12.371 - 12.372 - <div id="scene1.3.92">Prithee be cheerful: know'st thou not, the duke</div> 12.373 - 12.374 - <div id="scene1.3.93">Hath banish'd me, his daughter?</div> 12.375 - 12.376 - </div> 12.377 - 12.378 - <div id="speech37" class="character">ROSALIND</div> 12.379 - <div class="dialog"> 12.380 - <div id="scene1.3.94">That he hath not.</div> 12.381 - 12.382 - </div> 12.383 - 12.384 - <div id="speech38" class="character">CELIA</div> 12.385 - 12.386 - <div class="dialog"> 12.387 - 12.388 - <div id="scene1.3.95">No, hath not? Rosalind lacks then the love</div> 12.389 - 12.390 - <div id="scene1.3.96">Which teacheth thee that thou and I am one:</div> 12.391 - <div id="scene1.3.97">Shall we be sunder'd? shall we part, sweet girl?</div> 12.392 - 12.393 - <div id="scene1.3.98">No: let my father seek another heir.</div> 12.394 - 12.395 - <div id="scene1.3.99">Therefore devise with me how we may fly,</div> 12.396 - 12.397 - <div id="scene1.3.100">Whither to go and what to bear with us;</div> 12.398 - <div id="scene1.3.101">And do not seek to take your change upon you,</div> 12.399 - <div id="scene1.3.102">To bear your griefs yourself and leave me out;</div> 12.400 - 12.401 - <div id="scene1.3.103">For, by this heaven, now at our sorrows pale,</div> 12.402 - 12.403 - <div id="scene1.3.104">Say what thou canst, I'll go along with thee.</div> 12.404 - 12.405 - </div> 12.406 - 12.407 - <div id="speech39" class="character">ROSALIND</div> 12.408 - <div class="dialog"> 12.409 - 12.410 - <div id="scene1.3.105">Why, whither shall we go?</div> 12.411 - 12.412 - </div> 12.413 - 12.414 - <div id="speech40" class="character">CELIA</div> 12.415 - 12.416 - <div class="dialog"> 12.417 - <div id="scene1.3.106">To seek my uncle in the forest of Arden.</div> 12.418 - </div> 12.419 - 12.420 - <div id="speech41" class="character">ROSALIND</div> 12.421 - 12.422 - <div class="dialog"> 12.423 - 12.424 - <div id="scene1.3.107">Alas, what danger will it be to us,</div> 12.425 - 12.426 - <div id="scene1.3.108">Maids as we are, to travel forth so far!</div> 12.427 - <div id="scene1.3.109">Beauty provoketh thieves sooner than gold.</div> 12.428 - 12.429 - </div> 12.430 - 12.431 - <div id="speech42" class="character">CELIA</div> 12.432 - 12.433 - <div class="dialog"> 12.434 - <div id="scene1.3.110">I'll put myself in poor and mean attire</div> 12.435 - 12.436 - <div id="scene1.3.111">And with a kind of umber smirch my face;</div> 12.437 - <div id="scene1.3.112">The like do you: so shall we pass along</div> 12.438 - 12.439 - <div id="scene1.3.113">And never stir assailants.</div> 12.440 - 12.441 - </div> 12.442 - 12.443 - <div id="speech43" class="character">ROSALIND</div> 12.444 - <div class="dialog"> 12.445 - 12.446 - <div id="scene1.3.114">Were it not better,</div> 12.447 - 12.448 - <div id="scene1.3.115">Because that I am more than common tall,</div> 12.449 - 12.450 - <div id="scene1.3.116">That I did suit me all points like a man?</div> 12.451 - 12.452 - <div id="scene1.3.117">A gallant curtle-axe upon my thigh,</div> 12.453 - 12.454 - <div id="scene1.3.118">A boar-spear in my hand; and--in my heart</div> 12.455 - 12.456 - <div id="scene1.3.119">Lie there what hidden woman's fear there will--</div> 12.457 - 12.458 - <div id="scene1.3.120">We'll have a swashing and a martial outside,</div> 12.459 - 12.460 - <div id="scene1.3.121">As many other mannish cowards have</div> 12.461 - 12.462 - <div id="scene1.3.122">That do outface it with their semblances.</div> 12.463 - 12.464 - </div> 12.465 - 12.466 - <div id="speech44" class="character">CELIA</div> 12.467 - 12.468 - <div class="dialog"> 12.469 - 12.470 - <div id="scene1.3.123">What shall I call thee when thou art a man?</div> 12.471 - </div> 12.472 - 12.473 - <div id="speech45" class="character">ROSALIND</div> 12.474 - 12.475 - <div class="dialog"> 12.476 - <div id="scene1.3.124">I'll have no worse a name than Jove's own page;</div> 12.477 - 12.478 - <div id="scene1.3.125">And therefore look you call me Ganymede.</div> 12.479 - 12.480 - <div id="scene1.3.126">But what will you be call'd?</div> 12.481 - 12.482 - </div> 12.483 - 12.484 - <div id="speech46" class="character">CELIA</div> 12.485 - 12.486 - <div class="dialog"> 12.487 - 12.488 - <div id="scene1.3.127">Something that hath a reference to my state</div> 12.489 - <div id="scene1.3.128">No longer Celia, but Aliena.</div> 12.490 - 12.491 - </div> 12.492 - 12.493 - <div id="speech47" class="character">ROSALIND</div> 12.494 - <div class="dialog"> 12.495 - 12.496 - <div id="scene1.3.129">But, cousin, what if we assay'd to steal</div> 12.497 - 12.498 - <div id="scene1.3.130">The clownish fool out of your father's court?</div> 12.499 - 12.500 - <div id="scene1.3.131">Would he not be a comfort to our travel?</div> 12.501 - 12.502 - </div> 12.503 - 12.504 - <div id="speech48" class="character">CELIA</div> 12.505 - 12.506 - <div class="dialog"> 12.507 - 12.508 - <div id="scene1.3.132">He'll go along o'er the wide world with me;</div> 12.509 - 12.510 - <div id="scene1.3.133">Leave me alone to woo him. Let's away,</div> 12.511 - <div id="scene1.3.134">And get our jewels and our wealth together,</div> 12.512 - 12.513 - <div id="scene1.3.135">Devise the fittest time and safest way</div> 12.514 - 12.515 - <div id="scene1.3.136">To hide us from pursuit that will be made</div> 12.516 - 12.517 - <div id="scene1.3.137">After my flight. Now go we in content</div> 12.518 - 12.519 - <div id="scene1.3.138">To liberty and not to banishment.</div> 12.520 - <div class="direction">Exeunt</div> 12.521 - 12.522 - </div> 12.523 - 12.524 - </div> 12.525 - </div> 12.526 -</div> 12.527 -</body> 12.528 - 12.529 -</html> 12.530 \ No newline at end of file
13.1 --- a/lxml/test_css.py Tue Dec 30 18:36:08 2008 +0100 13.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 13.3 @@ -1,133 +0,0 @@ 13.4 -import unittest, sys 13.5 -from lxml.tests.common_imports import doctest, make_doctest, HelperTestCase 13.6 -from lxml import html 13.7 -from lxml import cssselect 13.8 -import os 13.9 - 13.10 -doc_fn = os.path.join(os.path.dirname(__file__), 13.11 - 'css_shakespear.html') 13.12 - 13.13 -try: 13.14 - basestring = __builtins__['basestring'] 13.15 -except (NameError, KeyError): 13.16 - basestring = (str, bytes) 13.17 - 13.18 -# Data borrowed from http://mootools.net/slickspeed/ 13.19 - 13.20 -class CSSTestCase(HelperTestCase): 13.21 - 13.22 - selectors = [ 13.23 - ## Changed from original; probably because I'm only searching the body 13.24 - #('*', 252), 13.25 - ('*', 246), 13.26 - ('div:only-child', 22), # ? 13.27 - ## Changed from original, because the original doesn't make sense. 13.28 - ## There really aren't that many occurrances of 'celia' 13.29 - #('div:contains(CELIA)', 243), 13.30 - ('div:contains(CELIA)', 30), 13.31 - ('div:nth-child(even)', 106), 13.32 - ('div:nth-child(2n)', 106), 13.33 - ('div:nth-child(odd)', 137), 13.34 - ('div:nth-child(2n+1)', 137), 13.35 - ('div:nth-child(n)', 243), 13.36 - ('div:last-child', 53), 13.37 - ('div:first-child', 51), 13.38 - ('div > div', 242), 13.39 - ('div + div', 190), 13.40 - ('div ~ div', 190), 13.41 - ('body', 1), 13.42 - ('body div', 243), 13.43 - ('div', 243), 13.44 - ('div div', 242), 13.45 - ('div div div', 241), 13.46 - ('div, div, div', 243), 13.47 - ('div, a, span', 243), 13.48 - ('.dialog', 51), 13.49 - ('div.dialog', 51), 13.50 - ('div .dialog', 51), 13.51 - ('div.character, div.dialog', 99), 13.52 - ('div.direction.dialog', 0), 13.53 - ('div.dialog.direction', 0), 13.54 - ('div.dialog.scene', 1), 13.55 - ('div.scene.scene', 1), 13.56 - ('div.scene .scene', 0), 13.57 - ('div.direction .dialog ', 0), 13.58 - ('div .dialog .direction', 4), 13.59 - ('div.dialog .dialog .direction', 4), 13.60 - ('#speech5', 1), 13.61 - ('div#speech5', 1), 13.62 - ('div #speech5', 1), 13.63 - ('div.scene div.dialog', 49), 13.64 - ('div#scene1 div.dialog div', 142), 13.65 - ('#scene1 #speech1', 1), 13.66 - ('div[class]', 103), 13.67 - ('div[class=dialog]', 50), 13.68 - ('div[class^=dia]', 51), 13.69 - ('div[class$=log]', 50), 13.70 - ('div[class*=sce]', 1), 13.71 - ('div[class|=dialog]', 50), # ? Seems right 13.72 - ('div[class!=madeup]', 243), # ? Seems right 13.73 - ('div[class~=dialog]', 51), # ? Seems right 13.74 - ] 13.75 - 13.76 - def __init__(self, index): 13.77 - self.index = index 13.78 - super(HelperTestCase, self).__init__() 13.79 - 13.80 - def all(cls): 13.81 - for i in range(len(cls.selectors)): 13.82 - yield cls(i) 13.83 - all = classmethod(all) 13.84 - 13.85 - def runTest(self): 13.86 - f = open(doc_fn, 'rb') 13.87 - c = f.read() 13.88 - f.close() 13.89 - doc = html.document_fromstring(c) 13.90 - body = doc.xpath('//body')[0] 13.91 - bad = [] 13.92 - selector, count = self.selectors[self.index] 13.93 - xpath = cssselect.css_to_xpath(cssselect.parse(selector)) 13.94 - try: 13.95 - results = body.xpath(xpath) 13.96 - except Exception: 13.97 - e = sys.exc_info()[1] 13.98 - e.args = ("%s for xpath %r" % (e, xpath)) 13.99 - raise 13.100 - found = {} 13.101 - for item in results: 13.102 - if item in found: 13.103 - assert 0, ( 13.104 - "Element shows up multiple times: %r" % item) 13.105 - found[item] = None 13.106 - if isinstance(results, basestring): 13.107 - assert 0, ( 13.108 - "Got string result (%r), not element, for xpath %r" 13.109 - % (results[:20], str(xpath))) 13.110 - if len(results) != count: 13.111 - #if self.shortDescription() == 'div.character, div.dialog': 13.112 - # import pdb; pdb.set_trace() 13.113 - assert 0, ( 13.114 - "Did not get expected results (%s) instead %s for xpath %r" 13.115 - % (count, len(results), str(xpath))) 13.116 - 13.117 - def shortDescription(self): 13.118 - return self.selectors[self.index][0] 13.119 - 13.120 -def unique(s): 13.121 - found = {} 13.122 - result = [] 13.123 - for item in s: 13.124 - if item in found: 13.125 - continue 13.126 - found[item] = None 13.127 - result.append(s) 13.128 - return result 13.129 - 13.130 -def test_suite(): 13.131 - suite = unittest.TestSuite() 13.132 - if sys.version_info >= (2,4): 13.133 - suite.addTests([make_doctest('test_css_select.txt')]) 13.134 - suite.addTests([make_doctest('test_css.txt')]) 13.135 - suite.addTests(list(CSSTestCase.all())) 13.136 - return suite
14.1 --- a/lxml/test_css.txt Tue Dec 30 18:36:08 2008 +0100 14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 14.3 @@ -1,140 +0,0 @@ 14.4 -A quick test of tokenizing: 14.5 - 14.6 - >>> from lxml.cssselect import tokenize, parse 14.7 - >>> def ptok(s): 14.8 - ... for item in tokenize(s): 14.9 - ... print(repr(item).replace("u'", "'")) 14.10 - >>> ptok('E > f[a~="y\\"x"]') 14.11 - Symbol('E', 0) 14.12 - Token('>', 2) 14.13 - Symbol('f', 4) 14.14 - Token('[', 5) 14.15 - Symbol('a', 6) 14.16 - Token('~=', 7) 14.17 - String('y"x', 9) 14.18 - Token(']', 15) 14.19 - 14.20 -Then of parsing: 14.21 - 14.22 - >>> parse('td.foo, .bar') 14.23 - Or([Class[Element[td].foo], CombinedSelector[Element[*] <followed> Class[Element[*].bar]]]) 14.24 - >>> parse('div, td.foo, div.bar span') 14.25 - Or([Element[div], Class[Element[td].foo], CombinedSelector[Class[Element[div].bar] <followed> Element[span]]]) 14.26 - >>> parse('div > p') 14.27 - CombinedSelector[Element[div] > Element[p]] 14.28 - >>> parse('td:first') 14.29 - Pseudo[Element[td]:first] 14.30 - >>> parse('a[name]') 14.31 - Attrib[Element[a][name]] 14.32 - >>> repr(parse('a[rel="include"]')).replace("u'", "'") 14.33 - "Attrib[Element[a][rel = String('include', 6)]]" 14.34 - >>> repr(parse('a[hreflang |= \'en\']')).replace("u'", "'") 14.35 - "Attrib[Element[a][hreflang |= String('en', 14)]]" 14.36 - >>> parse('div:nth-child(10)') 14.37 - Function[Element[div]:nth-child(10)] 14.38 - >>> parse('div:nth-of-type(10)') 14.39 - Function[Element[div]:nth-of-type(10)] 14.40 - >>> parse('div div:nth-of-type(10) .aclass') 14.41 - CombinedSelector[CombinedSelector[Element[div] <followed> Function[Element[div]:nth-of-type(10)]] <followed> Class[Element[*].aclass]] 14.42 - >>> parse('label:only') 14.43 - Pseudo[Element[label]:only] 14.44 - >>> parse('a:lang(fr)') 14.45 - Function[Element[a]:lang(Element[fr])] 14.46 - >>> repr(parse('div:contains("foo")')).replace("u'", "'") 14.47 - "Function[Element[div]:contains(String('foo', 13))]" 14.48 - >>> parse('div#foobar') 14.49 - Hash[Element[div]#foobar] 14.50 - >>> parse('div:not(div.foo)') 14.51 - Function[Element[div]:not(Class[Element[div].foo])] 14.52 - >>> parse('td ~ th') 14.53 - CombinedSelector[Element[td] ~ Element[th]] 14.54 - 14.55 -Now of translation: 14.56 - 14.57 - >>> def xpath(css): 14.58 - ... print(parse(css).xpath()) 14.59 - >>> xpath('*') 14.60 - * 14.61 - >>> xpath('E') 14.62 - e 14.63 - >>> xpath('E[foo]') 14.64 - e[@foo] 14.65 - >>> xpath('E[foo="bar"]') 14.66 - e[@foo = 'bar'] 14.67 - >>> xpath('E[foo~="bar"]') 14.68 - e[contains(concat(' ', normalize-space(@foo), ' '), ' bar ')] 14.69 - >>> xpath('E[foo^="bar"]') 14.70 - e[starts-with(@foo, 'bar')] 14.71 - >>> xpath('E[foo$="bar"]') 14.72 - e[substring(@foo, string-length(@foo)-2) = 'bar'] 14.73 - >>> xpath('E[foo*="bar"]') 14.74 - e[contains(@foo, 'bar')] 14.75 - >>> xpath('E[hreflang|="en"]') 14.76 - e[@hreflang = 'en' or starts-with(@hreflang, 'en-')] 14.77 - >>> #xpath('E:root') 14.78 - >>> xpath('E:nth-child(1)') 14.79 - */*[name() = 'e' and (position() = 1)] 14.80 - >>> xpath('E:nth-last-child(1)') 14.81 - */*[name() = 'e' and (position() = last() - 1)] 14.82 - >>> xpath('E:nth-last-child(2n+2)') 14.83 - */*[name() = 'e' and ((position() +2) mod -2 = 0 and position() < (last() -2))] 14.84 - >>> xpath('E:nth-of-type(1)') 14.85 - */e[position() = 1] 14.86 - >>> xpath('E:nth-last-of-type(1)') 14.87 - */e[position() = last() - 1] 14.88 - >>> xpath('E:nth-last-of-type(1)') 14.89 - */e[position() = last() - 1] 14.90 - >>> xpath('div E:nth-last-of-type(1) .aclass') 14.91 - div/descendant::e[position() = last() - 1]/descendant::*[contains(concat(' ', normalize-space(@class), ' '), ' aclass ')] 14.92 - >>> xpath('E:first-child') 14.93 - */*[name() = 'e' and (position() = 1)] 14.94 - >>> xpath('E:last-child') 14.95 - */*[name() = 'e' and (position() = last())] 14.96 - >>> xpath('E:first-of-type') 14.97 - */e[position() = 1] 14.98 - >>> xpath('E:last-of-type') 14.99 - */e[position() = last()] 14.100 - >>> xpath('E:only-child') 14.101 - */*[name() = 'e' and (last() = 1)] 14.102 - >>> xpath('E:only-of-type') 14.103 - e[last() = 1] 14.104 - >>> xpath('E:empty') 14.105 - e[not(*) and not(normalize-space())] 14.106 - >>> xpath('E:contains("foo")') 14.107 - e[contains(css:lower-case(string(.)), 'foo')] 14.108 - >>> xpath('E.warning') 14.109 - e[contains(concat(' ', normalize-space(@class), ' '), ' warning ')] 14.110 - >>> xpath('E#myid') 14.111 - e[@id = 'myid'] 14.112 - >>> xpath('E:not(:contains("foo"))') 14.113 - e[not(contains(css:lower-case(string(.)), 'foo'))] 14.114 - >>> xpath('E F') 14.115 - e/descendant::f 14.116 - >>> xpath('E > F') 14.117 - e/f 14.118 - >>> xpath('E + F') 14.119 - e/following-sibling::*[name() = 'f' and (position() = 1)] 14.120 - >>> xpath('E ~ F') 14.121 - e/following-sibling::f 14.122 - >>> xpath('div#container p') 14.123 - div[@id = 'container']/descendant::p 14.124 - >>> xpath('p *:only-of-type') 14.125 - Traceback (most recent call last): 14.126 - ... 14.127 - NotImplementedError: *:only-of-type is not implemented 14.128 - 14.129 -Then of parse_series: 14.130 - 14.131 - >>> from lxml.cssselect import parse_series 14.132 - >>> parse_series('1n+3') 14.133 - (1, 3) 14.134 - >>> parse_series('n-5') 14.135 - (1, -5) 14.136 - >>> parse_series('odd') 14.137 - (2, 1) 14.138 - >>> parse_series('3n') 14.139 - (3, 0) 14.140 - >>> parse_series('n') 14.141 - (1, 0) 14.142 - >>> parse_series('5') 14.143 - (0, 5)
15.1 --- a/lxml/test_css_select.txt Tue Dec 30 18:36:08 2008 +0100 15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 15.3 @@ -1,150 +0,0 @@ 15.4 -This is a test of CSS selectors. We setup a document we'll use for 15.5 -all our selections, and a function make querying simpler: 15.6 - 15.7 - >>> from lxml.cssselect import CSSSelector 15.8 - >>> from lxml.etree import HTML 15.9 - >>> doc = HTML(''' 15.10 - ... <html><head></head><body> 15.11 - ... <div id="outer-div"> 15.12 - ... <a id="name-anchor" name="foo"></a> 15.13 - ... <a id="tag-anchor" rel="tag" href="http://localhost/foo">link</a> 15.14 - ... <a id="nofollow-anchor" rel="nofollow" href="https://example.org">link</a> 15.15 - ... <ol id="first-ol" class="a b c"> 15.16 - ... <li id="first-li">content</li> 15.17 - ... <li id="second-li" lang="en-US"> 15.18 - ... <div id="li-div"> 15.19 - ... </div> 15.20 - ... </li> 15.21 - ... <li id="third-li" class="ab c"></li> 15.22 - ... <li id="fourth-li" class="ab 15.23 - ... c"></li> 15.24 - ... <li id="fifth-li"></li> 15.25 - ... <li id="sixth-li"></li> 15.26 - ... <li id="seventh-li"> </li> 15.27 - ... </ol> 15.28 - ... <p id="paragraph"> 15.29 - ... <b id="p-b">hi</b> <em id="p-em">there</em> 15.30 - ... <b id="p-b2">guy</b></p> 15.31 - ... <ol id="second-ol"> 15.32 - ... </ol> 15.33 - ... </div> 15.34 - ... <div id="foobar-div" foobar="ab bc 15.35 - ... cde"><span id="foobar-span"></span></div> 15.36 - ... </body></html>''') 15.37 - >>> order = {} 15.38 - >>> for count, el in enumerate(doc.getiterator()): 15.39 - ... order[el] = count 15.40 - >>> def select_ids(selector): 15.41 - ... items = CSSSelector(selector)(doc) 15.42 - ... if not items: 15.43 - ... return 'empty' 15.44 - ... items = CSSSelector(selector)(doc) 15.45 - ... items.sort(key=lambda el: order[el]) 15.46 - ... return ', '.join([el.get('id', 'nil') for el in items]) 15.47 - >>> def pcss(main, *selectors): 15.48 - ... result = select_ids(main) 15.49 - ... for selector in selectors: 15.50 - ... sel_result = select_ids(selector) 15.51 - ... if sel_result != result: 15.52 - ... print('Selector %r returns %s' % (selector, sel_result)) 15.53 - ... print(result) 15.54 - 15.55 -Now, the tests: 15.56 - 15.57 - >>> pcss('*') # doctest: +ELLIPSIS 15.58 - nil, nil, nil, outer-div, ... foobar-span 15.59 - >>> pcss('div') 15.60 - outer-div, li-div, foobar-div 15.61 - >>> pcss('a[name]') 15.62 - name-anchor 15.63 - >>> pcss('a[rel]') 15.64 - tag-anchor, nofollow-anchor 15.65 - >>> pcss('a[rel="tag"]') 15.66 - tag-anchor 15.67 - >>> pcss('a[href*="localhost"]') 15.68 - tag-anchor 15.69 - >>> pcss('a[href^="http"]') 15.70 - tag-anchor, nofollow-anchor 15.71 - >>> pcss('a[href^="http:"]') 15.72 - tag-anchor 15.73 - >>> pcss('a[href$="org"]') 15.74 - nofollow-anchor 15.75 - >>> pcss('div[foobar~="bc"]', 'div[foobar~="cde"]') 15.76 - foobar-div 15.77 - >>> pcss('div[foobar~="cd"]') 15.78 - empty 15.79 - >>> pcss('*[lang|="en"]', '*[lang|="en-US"]') 15.80 - second-li 15.81 - >>> pcss('*[lang|="e"]') 15.82 - empty 15.83 - >>> pcss('li:nth-child(3)') 15.84 - third-li 15.85 - >>> pcss('li:nth-child(10)') 15.86 - empty 15.87 - >>> pcss('li:nth-child(2n)', 'li:nth-child(even)', 'li:nth-child(2n+0)') 15.88 - second-li, fourth-li, sixth-li 15.89 - >>> pcss('li:nth-child(+2n+1)', 'li:nth-child(odd)') 15.90 - first-li, third-li, fifth-li, seventh-li 15.91 - >>> pcss('li:nth-child(2n+4)') 15.92 - fourth-li, sixth-li 15.93 - >>> # FIXME: I'm not 100% sure this is right: 15.94 - >>> pcss('li:nth-child(3n+1)') 15.95 - first-li, fourth-li, seventh-li 15.96 - >>> # FIXME: I'm not sure if nth-last-child(1) or nth-last-child(1) 15.97 - >>> # should be equivalent to nth-last-child() 15.98 - >>> pcss('li:nth-last-child()', 'li:nth-last-child(0)') 15.99 - seventh-li 15.100 - >>> pcss('li:nth-last-child(2n)', 'li:nth-last-child(even)') 15.101 - second-li, fourth-li, sixth-li 15.102 - >>> pcss('li:nth-last-child(2n+2)') 15.103 - second-li, fourth-li 15.104 - >>> pcss('ol:first-of-type') 15.105 - first-ol 15.106 - >>> pcss('ol:nth-child(1)') 15.107 - empty 15.108 - >>> pcss('ol:nth-of-type(2)') 15.109 - second-ol 15.110 - >>> # FIXME: like above, (1) or (2)? 15.111 - >>> pcss('ol:nth-last-of-type(1)') 15.112 - first-ol 15.113 - >>> pcss('span:only-child') 15.114 - foobar-span 15.115 - >>> pcss('li div:only-child') 15.116 - li-div 15.117 - >>> pcss('div *:only-child') 15.118 - foobar-span 15.119 - >>> pcss('p *:only-of-type') 15.120 - Traceback (most recent call last): 15.121 - ... 15.122 - NotImplementedError: *:only-of-type is not implemented 15.123 - >>> pcss('p:only-of-type') 15.124 - paragraph 15.125 - >>> pcss('a:empty') 15.126 - name-anchor 15.127 - >>> pcss('li:empty') 15.128 - third-li, fourth-li, fifth-li, sixth-li, seventh-li 15.129 - >>> pcss('*:contains("link")') 15.130 - nil, nil, outer-div, tag-anchor, nofollow-anchor 15.131 - >>> pcss('*:contains("E")') 15.132 - nil, nil, outer-div, first-ol, first-li, paragraph, p-em 15.133 - >>> pcss('.a', '.b', '*.a', 'ol.a') 15.134 - first-ol 15.135 - >>> pcss('.c', '*.c') 15.136 - first-ol, third-li, fourth-li 15.137 - >>> pcss('ol *.c', 'ol li.c', 'li ~ li.c', 'ol > li.c') 15.138 - third-li, fourth-li 15.139 - >>> pcss('#first-li', 'li#first-li', '*#first-li') 15.140 - first-li 15.141 - >>> # Need some tests of :not() 15.142 - >>> pcss('li div', 'li > div', 'div div') 15.143 - li-div 15.144 - >>> pcss('div > div') 15.145 - empty 15.146 - >>> pcss('div + div') 15.147 - foobar-div 15.148 - >>> pcss('a ~ a') 15.149 - tag-anchor, nofollow-anchor 15.150 - >>> pcss('a[rel="tag"] ~ a') 15.151 - nofollow-anchor 15.152 - >>> pcss('ol#first-ol li:last-child', 'ol#first-ol *:last-child') 15.153 - seventh-li
16.1 --- a/selectors.txt Tue Dec 30 18:36:08 2008 +0100 16.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 16.3 @@ -1,22 +0,0 @@ 16.4 -*, //* 16.5 -E, //E 16.6 -E F, //E//F 16.7 -E > F, //E/F 16.8 -E:first-child, //*[1]/self::E 16.9 -E:nth-child(3), //E[count(preceding-sibling::*)=3-1] 16.10 -E:lang(c), //E[@xml:lang='c' or starts-with(@xml:lang,'c-')] 16.11 -E + F, //E/following-sibling::*[1]/self::F 16.12 -E[foo], //E[@foo] 16.13 -E[foo="warning"], //E[@foo='warning'] 16.14 -E[foo~="warning"], //E[contains(concat(' ',@foo,' '),' warning ')] 16.15 -E[lang|="en"], //E[@lang='en' or starts-with(@lang,'en-')] 16.16 -E:not([foo="warning"]), //E[not(./self::*[@foo='warning'])] 16.17 -E:not(.warning), //E[not(./self::*[contains(concat(' ',@class,' '),' warning ')])] 16.18 -DIV.warning, //DIV[contains(concat(' ',@class,' '),' warning ')] 16.19 -E#myid, //E[@id='myid'] 16.20 -p > *, //p/* 16.21 -#foo, //*[@id='foo'] 16.22 -.foo, //*[contains(concat(' ',@class,' '),' foo ')] 16.23 -*[title], //*[@title] 16.24 -p > *:first-child, //p/*[1]/self::* 16.25 -p[bar] + .content-top[foo], //p[@bar]/following-sibling::*[1]/self::*[contains(concat(' ',@class,' '),' content-top ')][@foo]
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/src/AssemblyInfo.cs.in Tue Dec 30 22:26:25 2008 +0100 17.3 @@ -0,0 +1,15 @@ 17.4 +// 17.5 +// AssemblyInfo.cs.in for project 'css3-selectors' 17.6 +// 17.7 +// Authors: 17.8 +// Emanuele Aina <em@nerd.ocracy.org> 17.9 +// 17.10 + 17.11 +using System.Reflection; 17.12 +using System.Runtime.CompilerServices; 17.13 + 17.14 +[assembly: AssemblyVersion("@VERSION@")] 17.15 +[assembly: AssemblyTitle ("css3-selectors")] 17.16 +[assembly: AssemblyDescription ("css3-selectors - CSS Level 3 Selectors to XPath converter")] 17.17 +[assembly: AssemblyCopyright ("Copyright (c) 2008-2009 Emanuele Aina <em@nerd.ocracy.org>")] 17.18 +[assembly: AssemblyCompany ("Studio Associato Di Nunzio e Di Gregorio")]
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/src/Css3Selectors.cs Tue Dec 30 22:26:25 2008 +0100 18.3 @@ -0,0 +1,340 @@ 18.4 +/* CSS3 Selectors to XPath converter 18.5 + * 18.6 + * Emanuele Aina <em@nerd.ocracy.org> 18.7 + * 18.8 + * Inspired by HTML::Selector::XPath from Tatsuhiko Miyagawa 18.9 + * http://search.cpan.org/~miyagawa/HTML-Selector-XPath/lib/HTML/Selector/XPath.pm 18.10 + */ 18.11 +using System; 18.12 +using System.Collections.Generic; 18.13 +using System.Linq; 18.14 +using System.Text.RegularExpressions; 18.15 + 18.16 +namespace Css3Selectors { 18.17 + 18.18 +public static class Css { 18.19 + public class Element { 18.20 + public virtual string ToCss() { 18.21 + return ""; 18.22 + } 18.23 + 18.24 + public virtual string ToXPath() { 18.25 + return ""; 18.26 + } 18.27 + public override string ToString() { 18.28 + return base.ToString() + "(" + ToCss() + ")"; 18.29 + } 18.30 + } 18.31 + 18.32 + public class Combinator : Element { 18.33 + public string Mode { get; set; } 18.34 + public override string ToCss() { 18.35 + return Mode; 18.36 + } 18.37 + 18.38 + public override string ToXPath() { 18.39 + switch(Mode) { 18.40 + case " ": return "//"; 18.41 + case ">": return "/"; 18.42 + case "+": return "/following-sibling::*[1]/self::"; 18.43 + case "~": return "/following-sibling::"; 18.44 + } 18.45 + throw new Exception(); 18.46 + } 18.47 + } 18.48 + 18.49 + public class Comma : Element { 18.50 + } 18.51 + 18.52 + public class SimpleSelector : Element { 18.53 + } 18.54 + 18.55 + public class TypeSelector : SimpleSelector { 18.56 + public string Namespace { get; set; } 18.57 + public string Type { get; set; } 18.58 + public override string ToCss() { 18.59 + if(Namespace == null) 18.60 + return Type; 18.61 + return Namespace + "|" + Type; 18.62 + } 18.63 + 18.64 + public override string ToXPath() { 18.65 + if(Namespace == null) 18.66 + return Type; 18.67 + return Namespace + ":" + Type; 18.68 + } 18.69 + } 18.70 + 18.71 + public class UniversalSelector : SimpleSelector { 18.72 + public override string ToCss() { 18.73 + return "*"; 18.74 + } 18.75 + 18.76 + public override string ToXPath() { 18.77 + return "*"; 18.78 + } 18.79 + } 18.80 + 18.81 + public class AttributeSelector : SimpleSelector { 18.82 + public string Attribute { get; set; } 18.83 + public string Comparison { get; set; } 18.84 + public string Value { get; set; } 18.85 + public override string ToCss() { 18.86 + if(Comparison == null) 18.87 + return "[" + Attribute + "]"; 18.88 + return "[" + Attribute + Comparison + "'" + Value + "']"; 18.89 + } 18.90 + 18.91 + public override string ToXPath() { 18.92 + switch(Comparison) { 18.93 + case null: return "[@" + Attribute + "]"; 18.94 + case "=" : return "[@" + Attribute + "='" + Value + "']"; 18.95 + case "|=": return String.Format("[@{0}='{1}' or starts-with(@{0},'{1}-')]", Attribute, Value); 18.96 + case "~=": return String.Format("[contains(concat(' ',@{0},' '),' {1} ')]", Attribute, Value); 18.97 + case "^=": return String.Format("[starts-with(@{0},'{1}')]", Attribute, Value); 18.98 + // TODO: add the ends-with() XPath function 18.99 + case "$=": return String.Format("[ends-with(@{0},'{1}')]", Attribute, Value); 18.100 + case "*=": return String.Format("[contains(@{0},'{1}')]", Attribute, Value); 18.101 + // NOTE: non-standard! 18.102 + case "!=": return String.Format("[not(@{0}='{1}')]", Attribute, Value); 18.103 + } 18.104 + throw new Exception(); 18.105 + } 18.106 + } 18.107 + 18.108 + public class ClassSelector : SimpleSelector { 18.109 + public string Class { get; set; } 18.110 + public override string ToCss() { 18.111 + return "." + Class; 18.112 + } 18.113 + 18.114 + public override string ToXPath() { 18.115 + return String.Format("[contains(concat(' ',@class,' '),' {0} ')]", Class); 18.116 + } 18.117 + } 18.118 + 18.119 + public class IdSelector : SimpleSelector { 18.120 + public string Id { get; set; } 18.121 + public override string ToCss() { 18.122 + return "#" + Id; 18.123 + } 18.124 + 18.125 + public override string ToXPath() { 18.126 + return String.Format("[@id='{0}']", Id); 18.127 + } 18.128 + } 18.129 + 18.130 + public class ContentSelector : SimpleSelector { 18.131 + // This public class intentionally left blank 18.132 + // See http://www.w3.org/TR/css3-selectors/#content-selectors 18.133 + } 18.134 + 18.135 + public class PseudoClassSelector : SimpleSelector { 18.136 + public string PseudoClass { get; set; } 18.137 + 18.138 + public override string ToCss() { 18.139 + return ":"+PseudoClass; 18.140 + } 18.141 + 18.142 + public override string ToXPath() { 18.143 + switch(PseudoClass) { 18.144 + case "only-child": return "[count(../*)=1]"; 18.145 + case "first-child": return "*[1]/self::"; 18.146 + case "last-child": return "*[last()]/self::"; 18.147 + } 18.148 + throw new Exception(); 18.149 + } 18.150 + } 18.151 + 18.152 + public class PseudoClassSelector<T> : PseudoClassSelector { 18.153 + public T Argument { get; set; } 18.154 + 18.155 + private string ToCssString(object obj) { 18.156 + SimpleSelector selector = obj as SimpleSelector; 18.157 + 18.158 + if(selector != null) return selector.ToCss(); 18.159 + IEnumerable<Element> tokens = obj as IEnumerable<Css.Element>; 18.160 + 18.161 + if(tokens != null) return tokens.Select(t => t.ToCss()).Aggregate((a,b) => a+b); 18.162 + return obj.ToString(); 18.163 + } 18.164 + 18.165 + private string ToXPathString(object obj) { 18.166 + SimpleSelector selector = obj as SimpleSelector; 18.167 + if(selector != null) return selector.ToXPath(); 18.168 + 18.169 + IEnumerable<Element> tokens = obj as IEnumerable<Css.Element>; 18.170 + if(tokens != null) return Regex.Replace(tokens.ToXPath(), @"^//", "./self::"); 18.171 + 18.172 + return obj.ToString(); 18.173 + } 18.174 + 18.175 + public override string ToCss() { 18.176 + return ":"+PseudoClass + "(" + ToCssString(Argument) + ")"; 18.177 + } 18.178 + 18.179 + public override string ToXPath() { 18.180 + switch(PseudoClass) { 18.181 + case "not": return String.Format("[not({0})]", ToXPathString(Argument)); 18.182 + case "nth-child": return String.Format("[count(preceding-sibling::*)={0}-1]", Argument); 18.183 + case "lang": return String.Format("[@xml:lang='{0}' or starts-with(@xml:lang,'{0}-')]", Argument); 18.184 + // TODO: removed from the CSS3 draft, underspecified, check its behaviour against other implementations 18.185 + case "contains": return String.Format("[contains(text(),'{0}')]", Argument); 18.186 + } 18.187 + throw new Exception(); 18.188 + } 18.189 + } 18.190 + 18.191 + public class PseudoElement { 18.192 + } 18.193 + 18.194 + private static string Unquote(string s) { 18.195 + Regex singleQuotes = new Regex("'(.*)'"); 18.196 + Regex doubleQuotes = new Regex("\"(.*)\""); 18.197 + 18.198 + if(singleQuotes.Match(s).Success) 18.199 + return singleQuotes.Replace(s, "$1"); 18.200 + if(doubleQuotes.Match(s).Success) 18.201 + return doubleQuotes.Replace(s, "$1"); 18.202 + return s; 18.203 + } 18.204 + 18.205 + public static IEnumerable<Element> Parse(string selector) 18.206 + { 18.207 + string tmp = selector; 18.208 + 18.209 + var parseTable = new[] { 18.210 + // tag name/id/class 18.211 + new[]{@"^([#.]?)([a-z0-9\\*_-]+)((\|)([a-z0-9\\*_-]*))?", "Element"}, 18.212 + // pseudo classes 18.213 + new[]{@"^:([a-z0-9_-]+)(\((.*)\))?", "Pseudo" }, 18.214 + // attribute value match 18.215 + new[]{@"^\[\s*([^\~\|^$*!=\s]+)\s*(([~\|^$*!]?=)\s*(.+))?\]", "Attr"}, 18.216 + // adjacency/direct descendance 18.217 + new[]{@"^\s*([>+\~\s])\s*", "Combinator"}, 18.218 + // rules union 18.219 + new[]{@"^\s*,", "Comma"} 18.220 + }; 18.221 + 18.222 + while(!String.IsNullOrEmpty(tmp)) { 18.223 + Match match = null; 18.224 + string type = null; 18.225 + 18.226 + Console.WriteLine(" rest -> '{0}'", tmp); 18.227 + 18.228 + foreach(var item in parseTable) { 18.229 + Regex r = new Regex(item[0], RegexOptions.Compiled | RegexOptions.IgnoreCase); 18.230 + match = r.Match(tmp); 18.231 + if(match.Success) { 18.232 + type = item[1]; 18.233 + tmp = r.Replace(tmp, ""); 18.234 + break; 18.235 + } 18.236 + } 18.237 + 18.238 + Console.WriteLine(" {0} -> '{1}'", type, match.Value); 18.239 + switch(type) { 18.240 + case "Element": 18.241 + switch(match.Groups[1].Value) { 18.242 + case "": 18.243 + if(match.Groups[5].Success) { 18.244 + yield return new TypeSelector{Namespace = match.Groups[2].Value, Type = match.Groups[5].Value}; 18.245 + } 18.246 + else { 18.247 + yield return new TypeSelector{Type = match.Groups[2].Value}; 18.248 + } 18.249 + break; 18.250 + case "#": 18.251 + yield return new IdSelector{Id = match.Groups[2].Value}; 18.252 + break; 18.253 + case ".": 18.254 + yield return new ClassSelector{Class = match.Groups[2].Value}; 18.255 + break; 18.256 + } 18.257 + break; 18.258 + case "Attr": 18.259 + if(match.Groups[3].Success) { 18.260 + yield return new AttributeSelector{ 18.261 + Attribute = Unquote(match.Groups[1].Value.Trim()), 18.262 + Comparison = match.Groups[3].Value, 18.263 + Value = Unquote(match.Groups[4].Value)}; 18.264 + } 18.265 + else { 18.266 + yield return new AttributeSelector{Attribute = match.Groups[1].Value}; 18.267 + } 18.268 + break; 18.269 + case "Pseudo": 18.270 + if(match.Groups[3].Success) 18.271 + switch(match.Groups[1].Value) { 18.272 + case "not": 18.273 + IEnumerable<Element> negated = Parse(match.Groups[3].Value); 18.274 + yield return new PseudoClassSelector<IEnumerable<Element>> 18.275 + {PseudoClass = match.Groups[1].Value, Argument = negated}; 18.276 + break; 18.277 + default: 18.278 + yield return new PseudoClassSelector<string>{PseudoClass = match.Groups[1].Value, Argument = match.Groups[3].Value}; 18.279 + break; 18.280 + } 18.281 + else 18.282 + yield return new PseudoClassSelector{PseudoClass = match.Groups[1].Value }; 18.283 + break; 18.284 + case "Combinator": 18.285 + yield return new Combinator{Mode = match.Groups[1].Value}; 18.286 + break; 18.287 + case "Comma": 18.288 + yield return new Comma(); 18.289 + break; 18.290 + default: 18.291 + throw new ArgumentException(String.Format("invalid selector: {0}", selector)); 18.292 + } 18.293 + } 18.294 + } 18.295 + 18.296 + public static string ToXPath(this IEnumerable<Element> tokens) { 18.297 + var parts = new [] {"//", "*"}.ToList(); 18.298 + var index = 1; 18.299 + 18.300 + foreach(Element token in tokens) { 18.301 + if (token is SimpleSelector) { 18.302 + SimpleSelector selector = (SimpleSelector)token; 18.303 + 18.304 + if (selector is TypeSelector) { 18.305 + parts[index] = selector.ToXPath(); 18.306 + } 18.307 + else if (selector is PseudoClassSelector) { 18.308 + PseudoClassSelector pseudo = (PseudoClassSelector)selector; 18.309 + switch(pseudo.PseudoClass) { 18.310 + case "first-child": 18.311 + case "last-child": 18.312 + int last = parts.Count - 1; 18.313 + parts[last] = pseudo.ToXPath() + parts[last]; 18.314 + break; 18.315 + default: 18.316 + parts.Add(pseudo.ToXPath()); 18.317 + break; 18.318 + } 18.319 + } 18.320 + else { 18.321 + parts.Add(selector.ToXPath()); 18.322 + } 18.323 + } 18.324 + else if (token is Combinator) { 18.325 + Combinator combinator = (Combinator)token; 18.326 + parts.Add(combinator.ToXPath()); 18.327 + 18.328 + index = parts.Count; 18.329 + parts.Add("*"); 18.330 + } 18.331 + else if (token is Comma) { 18.332 + parts.Add(" | "); 18.333 + } 18.334 + else { 18.335 + throw new Exception(); 18.336 + } 18.337 + } 18.338 + 18.339 + return String.Join("", parts.ToArray()); 18.340 + } 18.341 +} 18.342 + 18.343 +}
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/src/Makefile.am Tue Dec 30 22:26:25 2008 +0100 19.3 @@ -0,0 +1,15 @@ 19.4 +css3selectorsdir=$(pkglibdir) 19.5 +css3selectors_SCRIPTS = css3-selectors.dll 19.6 +EXTRA_DIST = $(css3selectors_sources) $(css3selectors_sources_in) 19.7 +CLEANFILES = css3-selectors.dll 19.8 +DISTCLEANFILES = AssemblyInfo.cs Makefile.in 19.9 + 19.10 +css3selectors_sources_in = AssemblyInfo.cs.in 19.11 +css3selectors_generated_sources = $(css3selectors_sources_in:.in=) 19.12 +css3selectors_sources = Css3Selectors.cs 19.13 + 19.14 +css3selectors_build_sources = $(addprefix $(srcdir)/, $(css3selectors_sources)) 19.15 +css3selectors_build_sources += $(css3selectors_generated_sources) 19.16 + 19.17 +css3-selectors.dll: $(css3selectors_build_sources) 19.18 + $(MCS) -target:library -out:$@ $(css3selectors_build_sources)
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/tests/LxmlTestCssPy.cs Tue Dec 30 22:26:25 2008 +0100 20.3 @@ -0,0 +1,77 @@ 20.4 +/* Tests for the CSS3 Selectors to XPath converter 20.5 + * 20.6 + * Parse the tests in the test_css.py python file from lxml and run them 20.7 + * 20.8 + * Emanuele Aina <em@nerd.ocracy.org> 20.9 + */ 20.10 +using System; 20.11 +using System.Xml; 20.12 +using System.Collections.Generic; 20.13 +using System.Linq; 20.14 +using System.IO; 20.15 +using System.Text.RegularExpressions; 20.16 + 20.17 +using Css3Selectors; 20.18 + 20.19 +public static class LxmlTestCssPy { 20.20 + public static void Run() 20.21 + { 20.22 + XmlDocument doc = new XmlDocument(); 20.23 + doc.Load("tests/lxml/css_shakespear.html"); 20.24 + 20.25 + XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable); 20.26 + ns.AddNamespace("_", "http://www.w3.org/1999/xhtml"); 20.27 + 20.28 + // Remove the <head> tag, as it is not counted when matching in html 20.29 + XmlNode head = doc.SelectSingleNode("//_:head", ns); 20.30 + Console.WriteLine("{0} {1} {2}", head, ns.DefaultNamespace, doc.NameTable.Get(String.Empty)); 20.31 + head.ParentNode.RemoveChild(head); 20.32 + 20.33 + // Then match selectors starting from the <html> tag 20.34 + XmlNode html = doc.SelectSingleNode("//_:html", ns); 20.35 + 20.36 + using(TextReader py = new StreamReader("tests/lxml/test_css.py")) { 20.37 + string line; 20.38 + while ((line = py.ReadLine()) != null) { 20.39 + if(line.Trim().StartsWith("selectors")) 20.40 + break; 20.41 + } 20.42 + while ((line = py.ReadLine()) != null) { 20.43 + if(line.Trim().StartsWith("#")) 20.44 + continue; 20.45 + if(line.Trim().StartsWith("]")) 20.46 + break; 20.47 + Match m = Regex.Match(line, @"\('(.*)', (\d+)\)"); 20.48 + if(!m.Success) 20.49 + throw new Exception(); 20.50 + string selector = m.Groups[1].Value; 20.51 + int reference = Int32.Parse(m.Groups[2].Value); 20.52 + 20.53 + Console.WriteLine("selector, reference: {0}, {1}", selector, reference); 20.54 + 20.55 + int count = -1; 20.56 + string xpath = null; 20.57 + Exception exception = null; 20.58 + try { 20.59 + xpath = Css.Parse(selector).Select(s => { 20.60 + if (s is Css.TypeSelector) 20.61 + ((Css.TypeSelector)s).Namespace = "_"; 20.62 + return s; 20.63 + }).ToXPath(); 20.64 + // Prefix the xpath expression with the current node, otherwise the <html> tag 20.65 + // will be matched too 20.66 + count = html.SelectNodes("."+xpath, ns).Count; 20.67 + } catch (Exception e){ 20.68 + exception = e; 20.69 + } 20.70 + 20.71 + if(count == reference) 20.72 + Console.WriteLine("OK '{0}' -> '{1}' ({2})", selector, xpath, count); 20.73 + else if(exception != null) 20.74 + Console.WriteLine("ERROR on '{0}' -> '{1}' (got {2}, expected '{3}', {4})", selector, xpath, count, reference, exception); 20.75 + else 20.76 + Console.WriteLine("ERROR on '{0}' -> '{1}' (got {2}, expected '{3}')", selector, xpath, count, reference); 20.77 + } 20.78 + } 20.79 + } 20.80 +}
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/tests/Makefile.am Tue Dec 30 22:26:25 2008 +0100 21.3 @@ -0,0 +1,21 @@ 21.4 +testsrunnerdir=$(pkglibdir) 21.5 +noinst_SCRIPTS = testsrunner.exe 21.6 +EXTRA_DIST = $(testsrunner_sources) $(testsrunner_sources_in) 21.7 +CLEANFILES = testsrunner.exe 21.8 +DISTCLEANFILES = AssemblyInfo.cs Makefile.in 21.9 +testsrunner_references=System.Core $(srcdir)/../src/css3-selectors.dll 21.10 + 21.11 +#testsrunner_sources_in = AssemblyInfo.cs.in 21.12 +#testsrunner_generated_sources = $(testsrunner_sources_in:.in=) 21.13 +testsrunner_sources = TestsRunner.cs LxmlTestCssPy.cs 21.14 + 21.15 +testsrunner_build_references = $(addprefix -r:, $(testsrunner_references)) 21.16 + 21.17 +testsrunner_build_sources = $(addprefix $(srcdir)/, $(testsrunner_sources)) 21.18 +testsrunner_build_sources += $(testsrunner_generated_sources) 21.19 +#resources = $(srcdir)/../resources/dummy.txt 21.20 + 21.21 +embedded= $(foreach res,$(resources), $(addprefix -resource:,$(res)),$(notdir $(res))) 21.22 + 21.23 +testsrunner.exe: $(testsrunner_build_sources) $(resources) 21.24 + $(MCS) $(testsrunner_build_references) $(CSS3SELECTORS_LIBS) $(embedded) -out:$@ $(testsrunner_build_sources)
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/tests/TestsRunner.cs Tue Dec 30 22:26:25 2008 +0100 22.3 @@ -0,0 +1,31 @@ 22.4 +using System; 22.5 +using System.Collections.Generic; 22.6 +using System.Linq; 22.7 +using System.IO; 22.8 +using Css3Selectors; 22.9 + 22.10 +class TestRunner { 22.11 + public static void Main (string[] args) 22.12 + { 22.13 + using(TextReader selectors = new StreamReader("tests/selectors.txt")) { 22.14 + string line; 22.15 + while ((line = selectors.ReadLine()) != null) { 22.16 + string selector, reference, xpath; 22.17 + var split = line.Split(new[]{','}, 2); 22.18 + selector = split[0]; 22.19 + reference = split[1].Trim(); 22.20 + 22.21 + Console.WriteLine("CSS: {0}", selector); 22.22 + var tokens = Css.Parse(selector).ToList(); 22.23 + Console.WriteLine(" tokens -> {0}", tokens.Select(t => t.ToString()).Aggregate((a,b) => a+" "+b)); 22.24 + xpath = tokens.ToXPath(); 22.25 + if(xpath == reference) 22.26 + Console.WriteLine("OK '{0}' -> '{1}'", selector, reference); 22.27 + else 22.28 + Console.WriteLine("ERROR on '{0}' -> '{1}' (expected '{2}')", selector, xpath, reference); 22.29 + } 22.30 + } 22.31 + 22.32 + LxmlTestCssPy.Run(); 22.33 + } 22.34 +}
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/tests/lxml/css_shakespear.html Tue Dec 30 22:26:25 2008 +0100 23.3 @@ -0,0 +1,526 @@ 23.4 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 23.5 + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 23.6 + 23.7 +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" debug="true"> 23.8 +<head> 23.9 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 23.10 +</head> 23.11 + 23.12 +<body> 23.13 + 23.14 + <div id="test"> 23.15 + <div class="dialog"> 23.16 + <h2>As You Like It</h2> 23.17 + <div id="playwright"> 23.18 + 23.19 + by William Shakespeare 23.20 + 23.21 + 23.22 + </div> 23.23 + <div class="dialog scene thirdClass" id="scene1"> 23.24 + 23.25 + <h3>ACT I, SCENE III. A room in the palace.</h3> 23.26 + 23.27 + <div class="dialog"> 23.28 + <div class="direction">Enter CELIA and ROSALIND</div> 23.29 + 23.30 + </div> 23.31 + 23.32 + <div id="speech1" class="character">CELIA</div> 23.33 + 23.34 + <div class="dialog"> 23.35 + <div id="scene1.3.1">Why, cousin! why, Rosalind! Cupid have mercy! not a word?</div> 23.36 + 23.37 + </div> 23.38 + 23.39 + <div id="speech2" class="character">ROSALIND</div> 23.40 + 23.41 + <div class="dialog"> 23.42 + <div id="scene1.3.2">Not one to throw at a dog.</div> 23.43 + 23.44 + </div> 23.45 + 23.46 + <div id="speech3" class="character">CELIA</div> 23.47 + 23.48 + <div class="dialog"> 23.49 + <div id="scene1.3.3">No, thy words are too precious to be cast away upon</div> 23.50 + 23.51 + <div id="scene1.3.4">curs; throw some of them at me; come, lame me with reasons.</div> 23.52 + 23.53 + </div> 23.54 + 23.55 + <div id="speech4" class="character">ROSALIND</div> 23.56 + 23.57 + <div id="speech5" class="character">CELIA</div> 23.58 + 23.59 + <div class="dialog"> 23.60 + <div id="scene1.3.8">But is all this for your father?</div> 23.61 + 23.62 + </div> 23.63 + 23.64 + <div class="dialog"> 23.65 + <div id="scene1.3.5">Then there were two cousins laid up; when the one</div> 23.66 + <div id="scene1.3.6">should be lamed with reasons and the other mad</div> 23.67 + 23.68 + <div id="scene1.3.7">without any.</div> 23.69 + </div> 23.70 + 23.71 + <div id="speech6" class="character">ROSALIND</div> 23.72 + 23.73 + <div class="dialog"> 23.74 + <div id="scene1.3.9">No, some of it is for my child's father. O, how</div> 23.75 + 23.76 + <div id="scene1.3.10">full of briers is this working-day world!</div> 23.77 + 23.78 + </div> 23.79 + 23.80 + <div id="speech7" class="character">CELIA</div> 23.81 + 23.82 + <div class="dialog"> 23.83 + 23.84 + <div id="scene1.3.11">They are but burs, cousin, thrown upon thee in</div> 23.85 + <div id="scene1.3.12">holiday foolery: if we walk not in the trodden</div> 23.86 + 23.87 + <div id="scene1.3.13">paths our very petticoats will catch them.</div> 23.88 + 23.89 + </div> 23.90 + 23.91 + <div id="speech8" class="character">ROSALIND</div> 23.92 + 23.93 + <div class="dialog"> 23.94 + <div id="scene1.3.14">I could shake them off my coat: these burs are in my heart.</div> 23.95 + </div> 23.96 + 23.97 + <div id="speech9" class="character">CELIA</div> 23.98 + 23.99 + <div class="dialog"> 23.100 + <div id="scene1.3.15">Hem them away.</div> 23.101 + 23.102 + </div> 23.103 + 23.104 + <div id="speech10" class="character">ROSALIND</div> 23.105 + 23.106 + <div class="dialog"> 23.107 + 23.108 + <div id="scene1.3.16">I would try, if I could cry 'hem' and have him.</div> 23.109 + </div> 23.110 + 23.111 + <div id="speech11" class="character">CELIA</div> 23.112 + 23.113 + <div class="dialog"> 23.114 + <div id="scene1.3.17">Come, come, wrestle with thy affections.</div> 23.115 + 23.116 + </div> 23.117 + 23.118 + <div id="speech12" class="character">ROSALIND</div> 23.119 + <div class="dialog"> 23.120 + <div id="scene1.3.18">O, they take the part of a better wrestler than myself!</div> 23.121 + 23.122 + </div> 23.123 + 23.124 + <div id="speech13" class="character">CELIA</div> 23.125 + 23.126 + <div class="dialog"> 23.127 + 23.128 + <div id="scene1.3.19">O, a good wish upon you! you will try in time, in</div> 23.129 + <div id="scene1.3.20">despite of a fall. But, turning these jests out of</div> 23.130 + <div id="scene1.3.21">service, let us talk in good earnest: is it</div> 23.131 + 23.132 + <div id="scene1.3.22">possible, on such a sudden, you should fall into so</div> 23.133 + 23.134 + <div id="scene1.3.23">strong a liking with old Sir Rowland's youngest son?</div> 23.135 + 23.136 + </div> 23.137 + 23.138 + <div id="speech14" class="character">ROSALIND</div> 23.139 + <div class="dialog"> 23.140 + <div id="scene1.3.24">The duke my father loved his father dearly.</div> 23.141 + 23.142 + </div> 23.143 + 23.144 + <div id="speech15" class="character">CELIA</div> 23.145 + 23.146 + <div class="dialog"> 23.147 + 23.148 + <div id="scene1.3.25">Doth it therefore ensue that you should love his son</div> 23.149 + 23.150 + <div id="scene1.3.26">dearly? By this kind of chase, I should hate him,</div> 23.151 + 23.152 + <div id="scene1.3.27">for my father hated his father dearly; yet I hate</div> 23.153 + 23.154 + <div id="scene1.3.28">not Orlando.</div> 23.155 + 23.156 + </div> 23.157 + 23.158 + <div id="speech16" class="character">ROSALIND</div> 23.159 + 23.160 + <div title="wtf" class="dialog"> 23.161 + 23.162 + <div id="scene1.3.29">No, faith, hate him not, for my sake.</div> 23.163 + 23.164 + </div> 23.165 + 23.166 + <div id="speech17" class="character">CELIA</div> 23.167 + 23.168 + <div class="dialog"> 23.169 + <div id="scene1.3.30">Why should I not? doth he not deserve well?</div> 23.170 + 23.171 + </div> 23.172 + 23.173 + <div id="speech18" class="character">ROSALIND</div> 23.174 + 23.175 + <div class="dialog"> 23.176 + <div id="scene1.3.31">Let me love him for that, and do you love him</div> 23.177 + <div id="scene1.3.32">because I do. Look, here comes the duke.</div> 23.178 + </div> 23.179 + 23.180 + <div id="speech19" class="character">CELIA</div> 23.181 + 23.182 + <div class="dialog"> 23.183 + 23.184 + <div id="scene1.3.33">With his eyes full of anger.</div> 23.185 + <div class="direction">Enter DUKE FREDERICK, with Lords</div> 23.186 + </div> 23.187 + 23.188 + <div id="speech20" class="character">DUKE FREDERICK</div> 23.189 + 23.190 + <div class="dialog"> 23.191 + 23.192 + <div id="scene1.3.34">Mistress, dispatch you with your safest haste</div> 23.193 + 23.194 + <div id="scene1.3.35">And get you from our court.</div> 23.195 + </div> 23.196 + 23.197 + <div id="speech21" class="character">ROSALIND</div> 23.198 + 23.199 + <div class="dialog"> 23.200 + 23.201 + <div id="scene1.3.36">Me, uncle?</div> 23.202 + 23.203 + </div> 23.204 + 23.205 + <div id="speech22" class="character">DUKE FREDERICK</div> 23.206 + <div class="dialog"> 23.207 + <div id="scene1.3.37">You, cousin</div> 23.208 + 23.209 + <div id="scene1.3.38">Within these ten days if that thou be'st found</div> 23.210 + 23.211 + <div id="scene1.3.39">So near our public court as twenty miles,</div> 23.212 + 23.213 + <div id="scene1.3.40">Thou diest for it.</div> 23.214 + 23.215 + </div> 23.216 + 23.217 + <div id="speech23" class="character">ROSALIND</div> 23.218 + 23.219 + <div class="dialog"> 23.220 + 23.221 + <div id="scene1.3.41"> I do beseech your grace,</div> 23.222 + 23.223 + <div id="scene1.3.42">Let me the knowledge of my fault bear with me:</div> 23.224 + <div id="scene1.3.43">If with myself I hold intelligence</div> 23.225 + 23.226 + <div id="scene1.3.44">Or have acquaintance with mine own desires,</div> 23.227 + 23.228 + <div id="scene1.3.45">If that I do not dream or be not frantic,--</div> 23.229 + 23.230 + <div id="scene1.3.46">As I do trust I am not--then, dear uncle,</div> 23.231 + 23.232 + <div id="scene1.3.47">Never so much as in a thought unborn</div> 23.233 + 23.234 + <div id="scene1.3.48">Did I offend your highness.</div> 23.235 + 23.236 + </div> 23.237 + 23.238 + <div id="speech24" class="character">DUKE FREDERICK</div> 23.239 + 23.240 + <div class="dialog"> 23.241 + <div id="scene1.3.49">Thus do all traitors:</div> 23.242 + 23.243 + <div id="scene1.3.50">If their purgation did consist in words,</div> 23.244 + 23.245 + <div id="scene1.3.51">They are as innocent as grace itself:</div> 23.246 + 23.247 + <div id="scene1.3.52">Let it suffice thee that I trust thee not.</div> 23.248 + 23.249 + </div> 23.250 + 23.251 + <div id="speech25" class="character">ROSALIND</div> 23.252 + 23.253 + <div class="dialog"> 23.254 + 23.255 + <div id="scene1.3.53">Yet your mistrust cannot make me a traitor:</div> 23.256 + 23.257 + <div id="scene1.3.54">Tell me whereon the likelihood depends.</div> 23.258 + 23.259 + </div> 23.260 + 23.261 + <div id="speech26" class="character">DUKE FREDERICK</div> 23.262 + <div class="dialog"> 23.263 + 23.264 + <div id="scene1.3.55">Thou art thy father's daughter; there's enough.</div> 23.265 + 23.266 + </div> 23.267 + 23.268 + <div id="speech27" class="character">ROSALIND</div> 23.269 + 23.270 + <div class="dialog"> 23.271 + <div id="scene1.3.56">So was I when your highness took his dukedom;</div> 23.272 + <div id="scene1.3.57">So was I when your highness banish'd him:</div> 23.273 + 23.274 + <div id="scene1.3.58">Treason is not inherited, my lord;</div> 23.275 + 23.276 + <div id="scene1.3.59">Or, if we did derive it from our friends,</div> 23.277 + 23.278 + <div id="scene1.3.60">What's that to me? my father was no traitor:</div> 23.279 + 23.280 + <div id="scene1.3.61">Then, good my liege, mistake me not so much</div> 23.281 + <div id="scene1.3.62">To think my poverty is treacherous.</div> 23.282 + 23.283 + </div> 23.284 + 23.285 + <div id="speech28" class="character">CELIA</div> 23.286 + <div class="dialog"> 23.287 + 23.288 + <div id="scene1.3.63">Dear sovereign, hear me speak.</div> 23.289 + 23.290 + </div> 23.291 + 23.292 + <div id="speech29" class="character">DUKE FREDERICK</div> 23.293 + 23.294 + <div class="dialog"> 23.295 + <div id="scene1.3.64">Ay, Celia; we stay'd her for your sake,</div> 23.296 + <div id="scene1.3.65">Else had she with her father ranged along.</div> 23.297 + 23.298 + </div> 23.299 + 23.300 + <div id="speech30" class="character">CELIA</div> 23.301 + 23.302 + <div class="dialog"> 23.303 + 23.304 + <div id="scene1.3.66">I did not then entreat to have her stay;</div> 23.305 + <div id="scene1.3.67">It was your pleasure and your own remorse:</div> 23.306 + <div id="scene1.3.68">I was too young that time to value her;</div> 23.307 + 23.308 + <div id="scene1.3.69">But now I know her: if she be a traitor,</div> 23.309 + 23.310 + <div id="scene1.3.70">Why so am I; we still have slept together,</div> 23.311 + 23.312 + <div id="scene1.3.71">Rose at an instant, learn'd, play'd, eat together,</div> 23.313 + <div id="scene1.3.72">And wheresoever we went, like Juno's swans,</div> 23.314 + 23.315 + <div id="scene1.3.73">Still we went coupled and inseparable.</div> 23.316 + </div> 23.317 + 23.318 + <div id="speech31" class="character">DUKE FREDERICK</div> 23.319 + 23.320 + <div class="dialog"> 23.321 + <div id="scene1.3.74">She is too subtle for thee; and her smoothness,</div> 23.322 + <div id="scene1.3.75">Her very silence and her patience</div> 23.323 + <div id="scene1.3.76">Speak to the people, and they pity her.</div> 23.324 + <div id="scene1.3.77">Thou art a fool: she robs thee of thy name;</div> 23.325 + 23.326 + <div id="scene1.3.78">And thou wilt show more bright and seem more virtuous</div> 23.327 + 23.328 + <div id="scene1.3.79">When she is gone. Then open not thy lips:</div> 23.329 + <div id="scene1.3.80">Firm and irrevocable is my doom</div> 23.330 + <div id="scene1.3.81">Which I have pass'd upon her; she is banish'd.</div> 23.331 + </div> 23.332 + 23.333 + <div id="speech32" class="character">CELIA</div> 23.334 + 23.335 + <div class="dialog"> 23.336 + <div id="scene1.3.82">Pronounce that sentence then on me, my liege:</div> 23.337 + <div id="scene1.3.83">I cannot live out of her company.</div> 23.338 + </div> 23.339 + 23.340 + <div id="speech33" class="character">DUKE FREDERICK</div> 23.341 + 23.342 + <div class="dialog"> 23.343 + <div id="scene1.3.84">You are a fool. You, niece, provide yourself:</div> 23.344 + 23.345 + <div id="scene1.3.85">If you outstay the time, upon mine honour,</div> 23.346 + <div id="scene1.3.86">And in the greatness of my word, you die.</div> 23.347 + <div class="direction">Exeunt DUKE FREDERICK and Lords</div> 23.348 + </div> 23.349 + 23.350 + <div id="speech34" class="character">CELIA</div> 23.351 + <div class="dialog"> 23.352 + 23.353 + <div id="scene1.3.87">O my poor Rosalind, whither wilt thou go?</div> 23.354 + 23.355 + <div id="scene1.3.88">Wilt thou change fathers? I will give thee mine.</div> 23.356 + <div id="scene1.3.89">I charge thee, be not thou more grieved than I am.</div> 23.357 + 23.358 + </div> 23.359 + 23.360 + <div id="speech35" class="character">ROSALIND</div> 23.361 + 23.362 + <div class="dialog"> 23.363 + 23.364 + <div id="scene1.3.90">I have more cause.</div> 23.365 + </div> 23.366 + 23.367 + <div id="speech36" class="character">CELIA</div> 23.368 + 23.369 + <div class="dialog"> 23.370 + <div id="scene1.3.91"> Thou hast not, cousin;</div> 23.371 + 23.372 + <div id="scene1.3.92">Prithee be cheerful: know'st thou not, the duke</div> 23.373 + 23.374 + <div id="scene1.3.93">Hath banish'd me, his daughter?</div> 23.375 + 23.376 + </div> 23.377 + 23.378 + <div id="speech37" class="character">ROSALIND</div> 23.379 + <div class="dialog"> 23.380 + <div id="scene1.3.94">That he hath not.</div> 23.381 + 23.382 + </div> 23.383 + 23.384 + <div id="speech38" class="character">CELIA</div> 23.385 + 23.386 + <div class="dialog"> 23.387 + 23.388 + <div id="scene1.3.95">No, hath not? Rosalind lacks then the love</div> 23.389 + 23.390 + <div id="scene1.3.96">Which teacheth thee that thou and I am one:</div> 23.391 + <div id="scene1.3.97">Shall we be sunder'd? shall we part, sweet girl?</div> 23.392 + 23.393 + <div id="scene1.3.98">No: let my father seek another heir.</div> 23.394 + 23.395 + <div id="scene1.3.99">Therefore devise with me how we may fly,</div> 23.396 + 23.397 + <div id="scene1.3.100">Whither to go and what to bear with us;</div> 23.398 + <div id="scene1.3.101">And do not seek to take your change upon you,</div> 23.399 + <div id="scene1.3.102">To bear your griefs yourself and leave me out;</div> 23.400 + 23.401 + <div id="scene1.3.103">For, by this heaven, now at our sorrows pale,</div> 23.402 + 23.403 + <div id="scene1.3.104">Say what thou canst, I'll go along with thee.</div> 23.404 + 23.405 + </div> 23.406 + 23.407 + <div id="speech39" class="character">ROSALIND</div> 23.408 + <div class="dialog"> 23.409 + 23.410 + <div id="scene1.3.105">Why, whither shall we go?</div> 23.411 + 23.412 + </div> 23.413 + 23.414 + <div id="speech40" class="character">CELIA</div> 23.415 + 23.416 + <div class="dialog"> 23.417 + <div id="scene1.3.106">To seek my uncle in the forest of Arden.</div> 23.418 + </div> 23.419 + 23.420 + <div id="speech41" class="character">ROSALIND</div> 23.421 + 23.422 + <div class="dialog"> 23.423 + 23.424 + <div id="scene1.3.107">Alas, what danger will it be to us,</div> 23.425 + 23.426 + <div id="scene1.3.108">Maids as we are, to travel forth so far!</div> 23.427 + <div id="scene1.3.109">Beauty provoketh thieves sooner than gold.</div> 23.428 + 23.429 + </div> 23.430 + 23.431 + <div id="speech42" class="character">CELIA</div> 23.432 + 23.433 + <div class="dialog"> 23.434 + <div id="scene1.3.110">I'll put myself in poor and mean attire</div> 23.435 + 23.436 + <div id="scene1.3.111">And with a kind of umber smirch my face;</div> 23.437 + <div id="scene1.3.112">The like do you: so shall we pass along</div> 23.438 + 23.439 + <div id="scene1.3.113">And never stir assailants.</div> 23.440 + 23.441 + </div> 23.442 + 23.443 + <div id="speech43" class="character">ROSALIND</div> 23.444 + <div class="dialog"> 23.445 + 23.446 + <div id="scene1.3.114">Were it not better,</div> 23.447 + 23.448 + <div id="scene1.3.115">Because that I am more than common tall,</div> 23.449 + 23.450 + <div id="scene1.3.116">That I did suit me all points like a man?</div> 23.451 + 23.452 + <div id="scene1.3.117">A gallant curtle-axe upon my thigh,</div> 23.453 + 23.454 + <div id="scene1.3.118">A boar-spear in my hand; and--in my heart</div> 23.455 + 23.456 + <div id="scene1.3.119">Lie there what hidden woman's fear there will--</div> 23.457 + 23.458 + <div id="scene1.3.120">We'll have a swashing and a martial outside,</div> 23.459 + 23.460 + <div id="scene1.3.121">As many other mannish cowards have</div> 23.461 + 23.462 + <div id="scene1.3.122">That do outface it with their semblances.</div> 23.463 + 23.464 + </div> 23.465 + 23.466 + <div id="speech44" class="character">CELIA</div> 23.467 + 23.468 + <div class="dialog"> 23.469 + 23.470 + <div id="scene1.3.123">What shall I call thee when thou art a man?</div> 23.471 + </div> 23.472 + 23.473 + <div id="speech45" class="character">ROSALIND</div> 23.474 + 23.475 + <div class="dialog"> 23.476 + <div id="scene1.3.124">I'll have no worse a name than Jove's own page;</div> 23.477 + 23.478 + <div id="scene1.3.125">And therefore look you call me Ganymede.</div> 23.479 + 23.480 + <div id="scene1.3.126">But what will you be call'd?</div> 23.481 + 23.482 + </div> 23.483 + 23.484 + <div id="speech46" class="character">CELIA</div> 23.485 + 23.486 + <div class="dialog"> 23.487 + 23.488 + <div id="scene1.3.127">Something that hath a reference to my state</div> 23.489 + <div id="scene1.3.128">No longer Celia, but Aliena.</div> 23.490 + 23.491 + </div> 23.492 + 23.493 + <div id="speech47" class="character">ROSALIND</div> 23.494 + <div class="dialog"> 23.495 + 23.496 + <div id="scene1.3.129">But, cousin, what if we assay'd to steal</div> 23.497 + 23.498 + <div id="scene1.3.130">The clownish fool out of your father's court?</div> 23.499 + 23.500 + <div id="scene1.3.131">Would he not be a comfort to our travel?</div> 23.501 + 23.502 + </div> 23.503 + 23.504 + <div id="speech48" class="character">CELIA</div> 23.505 + 23.506 + <div class="dialog"> 23.507 + 23.508 + <div id="scene1.3.132">He'll go along o'er the wide world with me;</div> 23.509 + 23.510 + <div id="scene1.3.133">Leave me alone to woo him. Let's away,</div> 23.511 + <div id="scene1.3.134">And get our jewels and our wealth together,</div> 23.512 + 23.513 + <div id="scene1.3.135">Devise the fittest time and safest way</div> 23.514 + 23.515 + <div id="scene1.3.136">To hide us from pursuit that will be made</div> 23.516 + 23.517 + <div id="scene1.3.137">After my flight. Now go we in content</div> 23.518 + 23.519 + <div id="scene1.3.138">To liberty and not to banishment.</div> 23.520 + <div class="direction">Exeunt</div> 23.521 + 23.522 + </div> 23.523 + 23.524 + </div> 23.525 + </div> 23.526 +</div> 23.527 +</body> 23.528 + 23.529 +</html> 23.530 \ No newline at end of file
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/tests/lxml/test_css.py Tue Dec 30 22:26:25 2008 +0100 24.3 @@ -0,0 +1,133 @@ 24.4 +import unittest, sys 24.5 +from lxml.tests.common_imports import doctest, make_doctest, HelperTestCase 24.6 +from lxml import html 24.7 +from lxml import cssselect 24.8 +import os 24.9 + 24.10 +doc_fn = os.path.join(os.path.dirname(__file__), 24.11 + 'css_shakespear.html') 24.12 + 24.13 +try: 24.14 + basestring = __builtins__['basestring'] 24.15 +except (NameError, KeyError): 24.16 + basestring = (str, bytes) 24.17 + 24.18 +# Data borrowed from http://mootools.net/slickspeed/ 24.19 + 24.20 +class CSSTestCase(HelperTestCase): 24.21 + 24.22 + selectors = [ 24.23 + ## Changed from original; probably because I'm only searching the body 24.24 + #('*', 252), 24.25 + ('*', 246), 24.26 + ('div:only-child', 22), # ? 24.27 + ## Changed from original, because the original doesn't make sense. 24.28 + ## There really aren't that many occurrances of 'celia' 24.29 + #('div:contains(CELIA)', 243), 24.30 + ('div:contains(CELIA)', 30), 24.31 + ('div:nth-child(even)', 106), 24.32 + ('div:nth-child(2n)', 106), 24.33 + ('div:nth-child(odd)', 137), 24.34 + ('div:nth-child(2n+1)', 137), 24.35 + ('div:nth-child(n)', 243), 24.36 + ('div:last-child', 53), 24.37 + ('div:first-child', 51), 24.38 + ('div > div', 242), 24.39 + ('div + div', 190), 24.40 + ('div ~ div', 190), 24.41 + ('body', 1), 24.42 + ('body div', 243), 24.43 + ('div', 243), 24.44 + ('div div', 242), 24.45 + ('div div div', 241), 24.46 + ('div, div, div', 243), 24.47 + ('div, a, span', 243), 24.48 + ('.dialog', 51), 24.49 + ('div.dialog', 51), 24.50 + ('div .dialog', 51), 24.51 + ('div.character, div.dialog', 99), 24.52 + ('div.direction.dialog', 0), 24.53 + ('div.dialog.direction', 0), 24.54 + ('div.dialog.scene', 1), 24.55 + ('div.scene.scene', 1), 24.56 + ('div.scene .scene', 0), 24.57 + ('div.direction .dialog ', 0), 24.58 + ('div .dialog .direction', 4), 24.59 + ('div.dialog .dialog .direction', 4), 24.60 + ('#speech5', 1), 24.61 + ('div#speech5', 1), 24.62 + ('div #speech5', 1), 24.63 + ('div.scene div.dialog', 49), 24.64 + ('div#scene1 div.dialog div', 142), 24.65 + ('#scene1 #speech1', 1), 24.66 + ('div[class]', 103), 24.67 + ('div[class=dialog]', 50), 24.68 + ('div[class^=dia]', 51), 24.69 + ('div[class$=log]', 50), 24.70 + ('div[class*=sce]', 1), 24.71 + ('div[class|=dialog]', 50), # ? Seems right 24.72 + ('div[class!=madeup]', 243), # ? Seems right 24.73 + ('div[class~=dialog]', 51), # ? Seems right 24.74 + ] 24.75 + 24.76 + def __init__(self, index): 24.77 + self.index = index 24.78 + super(HelperTestCase, self).__init__() 24.79 + 24.80 + def all(cls): 24.81 + for i in range(len(cls.selectors)): 24.82 + yield cls(i) 24.83 + all = classmethod(all) 24.84 + 24.85 + def runTest(self): 24.86 + f = open(doc_fn, 'rb') 24.87 + c = f.read() 24.88 + f.close() 24.89 + doc = html.document_fromstring(c) 24.90 + body = doc.xpath('//body')[0] 24.91 + bad = [] 24.92 + selector, count = self.selectors[self.index] 24.93 + xpath = cssselect.css_to_xpath(cssselect.parse(selector)) 24.94 + try: 24.95 + results = body.xpath(xpath) 24.96 + except Exception: 24.97 + e = sys.exc_info()[1] 24.98 + e.args = ("%s for xpath %r" % (e, xpath)) 24.99 + raise 24.100 + found = {} 24.101 + for item in results: 24.102 + if item in found: 24.103 + assert 0, ( 24.104 + "Element shows up multiple times: %r" % item) 24.105 + found[item] = None 24.106 + if isinstance(results, basestring): 24.107 + assert 0, ( 24.108 + "Got string result (%r), not element, for xpath %r" 24.109 + % (results[:20], str(xpath))) 24.110 + if len(results) != count: 24.111 + #if self.shortDescription() == 'div.character, div.dialog': 24.112 + # import pdb; pdb.set_trace() 24.113 + assert 0, ( 24.114 + "Did not get expected results (%s) instead %s for xpath %r" 24.115 + % (count, len(results), str(xpath))) 24.116 + 24.117 + def shortDescription(self): 24.118 + return self.selectors[self.index][0] 24.119 + 24.120 +def unique(s): 24.121 + found = {} 24.122 + result = [] 24.123 + for item in s: 24.124 + if item in found: 24.125 + continue 24.126 + found[item] = None 24.127 + result.append(s) 24.128 + return result 24.129 + 24.130 +def test_suite(): 24.131 + suite = unittest.TestSuite() 24.132 + if sys.version_info >= (2,4): 24.133 + suite.addTests([make_doctest('test_css_select.txt')]) 24.134 + suite.addTests([make_doctest('test_css.txt')]) 24.135 + suite.addTests(list(CSSTestCase.all())) 24.136 + return suite
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/tests/lxml/test_css.txt Tue Dec 30 22:26:25 2008 +0100 25.3 @@ -0,0 +1,140 @@ 25.4 +A quick test of tokenizing: 25.5 + 25.6 + >>> from lxml.cssselect import tokenize, parse 25.7 + >>> def ptok(s): 25.8 + ... for item in tokenize(s): 25.9 + ... print(repr(item).replace("u'", "'")) 25.10 + >>> ptok('E > f[a~="y\\"x"]') 25.11 + Symbol('E', 0) 25.12 + Token('>', 2) 25.13 + Symbol('f', 4) 25.14 + Token('[', 5) 25.15 + Symbol('a', 6) 25.16 + Token('~=', 7) 25.17 + String('y"x', 9) 25.18 + Token(']', 15) 25.19 + 25.20 +Then of parsing: 25.21 + 25.22 + >>> parse('td.foo, .bar') 25.23 + Or([Class[Element[td].foo], CombinedSelector[Element[*] <followed> Class[Element[*].bar]]]) 25.24 + >>> parse('div, td.foo, div.bar span') 25.25 + Or([Element[div], Class[Element[td].foo], CombinedSelector[Class[Element[div].bar] <followed> Element[span]]]) 25.26 + >>> parse('div > p') 25.27 + CombinedSelector[Element[div] > Element[p]] 25.28 + >>> parse('td:first') 25.29 + Pseudo[Element[td]:first] 25.30 + >>> parse('a[name]') 25.31 + Attrib[Element[a][name]] 25.32 + >>> repr(parse('a[rel="include"]')).replace("u'", "'") 25.33 + "Attrib[Element[a][rel = String('include', 6)]]" 25.34 + >>> repr(parse('a[hreflang |= \'en\']')).replace("u'", "'") 25.35 + "Attrib[Element[a][hreflang |= String('en', 14)]]" 25.36 + >>> parse('div:nth-child(10)') 25.37 + Function[Element[div]:nth-child(10)] 25.38 + >>> parse('div:nth-of-type(10)') 25.39 + Function[Element[div]:nth-of-type(10)] 25.40 + >>> parse('div div:nth-of-type(10) .aclass') 25.41 + CombinedSelector[CombinedSelector[Element[div] <followed> Function[Element[div]:nth-of-type(10)]] <followed> Class[Element[*].aclass]] 25.42 + >>> parse('label:only') 25.43 + Pseudo[Element[label]:only] 25.44 + >>> parse('a:lang(fr)') 25.45 + Function[Element[a]:lang(Element[fr])] 25.46 + >>> repr(parse('div:contains("foo")')).replace("u'", "'") 25.47 + "Function[Element[div]:contains(String('foo', 13))]" 25.48 + >>> parse('div#foobar') 25.49 + Hash[Element[div]#foobar] 25.50 + >>> parse('div:not(div.foo)') 25.51 + Function[Element[div]:not(Class[Element[div].foo])] 25.52 + >>> parse('td ~ th') 25.53 + CombinedSelector[Element[td] ~ Element[th]] 25.54 + 25.55 +Now of translation: 25.56 + 25.57 + >>> def xpath(css): 25.58 + ... print(parse(css).xpath()) 25.59 + >>> xpath('*') 25.60 + * 25.61 + >>> xpath('E') 25.62 + e 25.63 + >>> xpath('E[foo]') 25.64 + e[@foo] 25.65 + >>> xpath('E[foo="bar"]') 25.66 + e[@foo = 'bar'] 25.67 + >>> xpath('E[foo~="bar"]') 25.68 + e[contains(concat(' ', normalize-space(@foo), ' '), ' bar ')] 25.69 + >>> xpath('E[foo^="bar"]') 25.70 + e[starts-with(@foo, 'bar')] 25.71 + >>> xpath('E[foo$="bar"]') 25.72 + e[substring(@foo, string-length(@foo)-2) = 'bar'] 25.73 + >>> xpath('E[foo*="bar"]') 25.74 + e[contains(@foo, 'bar')] 25.75 + >>> xpath('E[hreflang|="en"]') 25.76 + e[@hreflang = 'en' or starts-with(@hreflang, 'en-')] 25.77 + >>> #xpath('E:root') 25.78 + >>> xpath('E:nth-child(1)') 25.79 + */*[name() = 'e' and (position() = 1)] 25.80 + >>> xpath('E:nth-last-child(1)') 25.81 + */*[name() = 'e' and (position() = last() - 1)] 25.82 + >>> xpath('E:nth-last-child(2n+2)') 25.83 + */*[name() = 'e' and ((position() +2) mod -2 = 0 and position() < (last() -2))] 25.84 + >>> xpath('E:nth-of-type(1)') 25.85 + */e[position() = 1] 25.86 + >>> xpath('E:nth-last-of-type(1)') 25.87 + */e[position() = last() - 1] 25.88 + >>> xpath('E:nth-last-of-type(1)') 25.89 + */e[position() = last() - 1] 25.90 + >>> xpath('div E:nth-last-of-type(1) .aclass') 25.91 + div/descendant::e[position() = last() - 1]/descendant::*[contains(concat(' ', normalize-space(@class), ' '), ' aclass ')] 25.92 + >>> xpath('E:first-child') 25.93 + */*[name() = 'e' and (position() = 1)] 25.94 + >>> xpath('E:last-child') 25.95 + */*[name() = 'e' and (position() = last())] 25.96 + >>> xpath('E:first-of-type') 25.97 + */e[position() = 1] 25.98 + >>> xpath('E:last-of-type') 25.99 + */e[position() = last()] 25.100 + >>> xpath('E:only-child') 25.101 + */*[name() = 'e' and (last() = 1)] 25.102 + >>> xpath('E:only-of-type') 25.103 + e[last() = 1] 25.104 + >>> xpath('E:empty') 25.105 + e[not(*) and not(normalize-space())] 25.106 + >>> xpath('E:contains("foo")') 25.107 + e[contains(css:lower-case(string(.)), 'foo')] 25.108 + >>> xpath('E.warning') 25.109 + e[contains(concat(' ', normalize-space(@class), ' '), ' warning ')] 25.110 + >>> xpath('E#myid') 25.111 + e[@id = 'myid'] 25.112 + >>> xpath('E:not(:contains("foo"))') 25.113 + e[not(contains(css:lower-case(string(.)), 'foo'))] 25.114 + >>> xpath('E F') 25.115 + e/descendant::f 25.116 + >>> xpath('E > F') 25.117 + e/f 25.118 + >>> xpath('E + F') 25.119 + e/following-sibling::*[name() = 'f' and (position() = 1)] 25.120 + >>> xpath('E ~ F') 25.121 + e/following-sibling::f 25.122 + >>> xpath('div#container p') 25.123 + div[@id = 'container']/descendant::p 25.124 + >>> xpath('p *:only-of-type') 25.125 + Traceback (most recent call last): 25.126 + ... 25.127 + NotImplementedError: *:only-of-type is not implemented 25.128 + 25.129 +Then of parse_series: 25.130 + 25.131 + >>> from lxml.cssselect import parse_series 25.132 + >>> parse_series('1n+3') 25.133 + (1, 3) 25.134 + >>> parse_series('n-5') 25.135 + (1, -5) 25.136 + >>> parse_series('odd') 25.137 + (2, 1) 25.138 + >>> parse_series('3n') 25.139 + (3, 0) 25.140 + >>> parse_series('n') 25.141 + (1, 0) 25.142 + >>> parse_series('5') 25.143 + (0, 5)
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/tests/lxml/test_css_select.txt Tue Dec 30 22:26:25 2008 +0100 26.3 @@ -0,0 +1,150 @@ 26.4 +This is a test of CSS selectors. We setup a document we'll use for 26.5 +all our selections, and a function make querying simpler: 26.6 + 26.7 + >>> from lxml.cssselect import CSSSelector 26.8 + >>> from lxml.etree import HTML 26.9 + >>> doc = HTML(''' 26.10 + ... <html><head></head><body> 26.11 + ... <div id="outer-div"> 26.12 + ... <a id="name-anchor" name="foo"></a> 26.13 + ... <a id="tag-anchor" rel="tag" href="http://localhost/foo">link</a> 26.14 + ... <a id="nofollow-anchor" rel="nofollow" href="https://example.org">link</a> 26.15 + ... <ol id="first-ol" class="a b c"> 26.16 + ... <li id="first-li">content</li> 26.17 + ... <li id="second-li" lang="en-US"> 26.18 + ... <div id="li-div"> 26.19 + ... </div> 26.20 + ... </li> 26.21 + ... <li id="third-li" class="ab c"></li> 26.22 + ... <li id="fourth-li" class="ab 26.23 + ... c"></li> 26.24 + ... <li id="fifth-li"></li> 26.25 + ... <li id="sixth-li"></li> 26.26 + ... <li id="seventh-li"> </li> 26.27 + ... </ol> 26.28 + ... <p id="paragraph"> 26.29 + ... <b id="p-b">hi</b> <em id="p-em">there</em> 26.30 + ... <b id="p-b2">guy</b></p> 26.31 + ... <ol id="second-ol"> 26.32 + ... </ol> 26.33 + ... </div> 26.34 + ... <div id="foobar-div" foobar="ab bc 26.35 + ... cde"><span id="foobar-span"></span></div> 26.36 + ... </body></html>''') 26.37 + >>> order = {} 26.38 + >>> for count, el in enumerate(doc.getiterator()): 26.39 + ... order[el] = count 26.40 + >>> def select_ids(selector): 26.41 + ... items = CSSSelector(selector)(doc) 26.42 + ... if not items: 26.43 + ... return 'empty' 26.44 + ... items = CSSSelector(selector)(doc) 26.45 + ... items.sort(key=lambda el: order[el]) 26.46 + ... return ', '.join([el.get('id', 'nil') for el in items]) 26.47 + >>> def pcss(main, *selectors): 26.48 + ... result = select_ids(main) 26.49 + ... for selector in selectors: 26.50 + ... sel_result = select_ids(selector) 26.51 + ... if sel_result != result: 26.52 + ... print('Selector %r returns %s' % (selector, sel_result)) 26.53 + ... print(result) 26.54 + 26.55 +Now, the tests: 26.56 + 26.57 + >>> pcss('*') # doctest: +ELLIPSIS 26.58 + nil, nil, nil, outer-div, ... foobar-span 26.59 + >>> pcss('div') 26.60 + outer-div, li-div, foobar-div 26.61 + >>> pcss('a[name]') 26.62 + name-anchor 26.63 + >>> pcss('a[rel]') 26.64 + tag-anchor, nofollow-anchor 26.65 + >>> pcss('a[rel="tag"]') 26.66 + tag-anchor 26.67 + >>> pcss('a[href*="localhost"]') 26.68 + tag-anchor 26.69 + >>> pcss('a[href^="http"]') 26.70 + tag-anchor, nofollow-anchor 26.71 + >>> pcss('a[href^="http:"]') 26.72 + tag-anchor 26.73 + >>> pcss('a[href$="org"]') 26.74 + nofollow-anchor 26.75 + >>> pcss('div[foobar~="bc"]', 'div[foobar~="cde"]') 26.76 + foobar-div 26.77 + >>> pcss('div[foobar~="cd"]') 26.78 + empty 26.79 + >>> pcss('*[lang|="en"]', '*[lang|="en-US"]') 26.80 + second-li 26.81 + >>> pcss('*[lang|="e"]') 26.82 + empty 26.83 + >>> pcss('li:nth-child(3)') 26.84 + third-li 26.85 + >>> pcss('li:nth-child(10)') 26.86 + empty 26.87 + >>> pcss('li:nth-child(2n)', 'li:nth-child(even)', 'li:nth-child(2n+0)') 26.88 + second-li, fourth-li, sixth-li 26.89 + >>> pcss('li:nth-child(+2n+1)', 'li:nth-child(odd)') 26.90 + first-li, third-li, fifth-li, seventh-li 26.91 + >>> pcss('li:nth-child(2n+4)') 26.92 + fourth-li, sixth-li 26.93 + >>> # FIXME: I'm not 100% sure this is right: 26.94 + >>> pcss('li:nth-child(3n+1)') 26.95 + first-li, fourth-li, seventh-li 26.96 + >>> # FIXME: I'm not sure if nth-last-child(1) or nth-last-child(1) 26.97 + >>> # should be equivalent to nth-last-child() 26.98 + >>> pcss('li:nth-last-child()', 'li:nth-last-child(0)') 26.99 + seventh-li 26.100 + >>> pcss('li:nth-last-child(2n)', 'li:nth-last-child(even)') 26.101 + second-li, fourth-li, sixth-li 26.102 + >>> pcss('li:nth-last-child(2n+2)') 26.103 + second-li, fourth-li 26.104 + >>> pcss('ol:first-of-type') 26.105 + first-ol 26.106 + >>> pcss('ol:nth-child(1)') 26.107 + empty 26.108 + >>> pcss('ol:nth-of-type(2)') 26.109 + second-ol 26.110 + >>> # FIXME: like above, (1) or (2)? 26.111 + >>> pcss('ol:nth-last-of-type(1)') 26.112 + first-ol 26.113 + >>> pcss('span:only-child') 26.114 + foobar-span 26.115 + >>> pcss('li div:only-child') 26.116 + li-div 26.117 + >>> pcss('div *:only-child') 26.118 + foobar-span 26.119 + >>> pcss('p *:only-of-type') 26.120 + Traceback (most recent call last): 26.121 + ... 26.122 + NotImplementedError: *:only-of-type is not implemented 26.123 + >>> pcss('p:only-of-type') 26.124 + paragraph 26.125 + >>> pcss('a:empty') 26.126 + name-anchor 26.127 + >>> pcss('li:empty') 26.128 + third-li, fourth-li, fifth-li, sixth-li, seventh-li 26.129 + >>> pcss('*:contains("link")') 26.130 + nil, nil, outer-div, tag-anchor, nofollow-anchor 26.131 + >>> pcss('*:contains("E")') 26.132 + nil, nil, outer-div, first-ol, first-li, paragraph, p-em 26.133 + >>> pcss('.a', '.b', '*.a', 'ol.a') 26.134 + first-ol 26.135 + >>> pcss('.c', '*.c') 26.136 + first-ol, third-li, fourth-li 26.137 + >>> pcss('ol *.c', 'ol li.c', 'li ~ li.c', 'ol > li.c') 26.138 + third-li, fourth-li 26.139 + >>> pcss('#first-li', 'li#first-li', '*#first-li') 26.140 + first-li 26.141 + >>> # Need some tests of :not() 26.142 + >>> pcss('li div', 'li > div', 'div div') 26.143 + li-div 26.144 + >>> pcss('div > div') 26.145 + empty 26.146 + >>> pcss('div + div') 26.147 + foobar-div 26.148 + >>> pcss('a ~ a') 26.149 + tag-anchor, nofollow-anchor 26.150 + >>> pcss('a[rel="tag"] ~ a') 26.151 + nofollow-anchor 26.152 + >>> pcss('ol#first-ol li:last-child', 'ol#first-ol *:last-child') 26.153 + seventh-li
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/tests/selectors.txt Tue Dec 30 22:26:25 2008 +0100 27.3 @@ -0,0 +1,22 @@ 27.4 +*, //* 27.5 +E, //E 27.6 +E F, //E//F 27.7 +E > F, //E/F 27.8 +E:first-child, //*[1]/self::E 27.9 +E:nth-child(3), //E[count(preceding-sibling::*)=3-1] 27.10 +E:lang(c), //E[@xml:lang='c' or starts-with(@xml:lang,'c-')] 27.11 +E + F, //E/following-sibling::*[1]/self::F 27.12 +E[foo], //E[@foo] 27.13 +E[foo="warning"], //E[@foo='warning'] 27.14 +E[foo~="warning"], //E[contains(concat(' ',@foo,' '),' warning ')] 27.15 +E[lang|="en"], //E[@lang='en' or starts-with(@lang,'en-')] 27.16 +E:not([foo="warning"]), //E[not(./self::*[@foo='warning'])] 27.17 +E:not(.warning), //E[not(./self::*[contains(concat(' ',@class,' '),' warning ')])] 27.18 +DIV.warning, //DIV[contains(concat(' ',@class,' '),' warning ')] 27.19 +E#myid, //E[@id='myid'] 27.20 +p > *, //p/* 27.21 +#foo, //*[@id='foo'] 27.22 +.foo, //*[contains(concat(' ',@class,' '),' foo ')] 27.23 +*[title], //*[@title] 27.24 +p > *:first-child, //p/*[1]/self::* 27.25 +p[bar] + .content-top[foo], //p[@bar]/following-sibling::*[1]/self::*[contains(concat(' ',@class,' '),' content-top ')][@foo]
