[gxml: 2/25] Fix selectors parser



commit 6fc33dbf2439e024d3d75d661e80990a300ee2fa
Author: BZHDeveloper <inizan yannick gmail com>
Date:   Thu Oct 25 16:25:46 2018 +0200

    Fix selectors parser

 debian/Makefile.am            |  10 ---
 debian/changelog.in           |  11 ----
 debian/compat                 |   1 -
 debian/control.in             |  52 ----------------
 debian/copyright.in           |  46 --------------
 debian/gir1.2-gxml.install.in |   1 -
 debian/libgxml-0.install.in   |   1 -
 debian/libgxml-dev.install.in |   6 --
 debian/rules                  |   8 ---
 debian/source/format          |   1 -
 gxml/CssSelectorParser.vala   | 138 +++++++++++++++++++++++++++++++++++++-----
 test/CssSelectorTest.vala     |   4 +-
 12 files changed, 124 insertions(+), 155 deletions(-)
---
diff --git a/gxml/CssSelectorParser.vala b/gxml/CssSelectorParser.vala
index fab24d5..553ece3 100644
--- a/gxml/CssSelectorParser.vala
+++ b/gxml/CssSelectorParser.vala
@@ -116,13 +116,19 @@ public class GXml.CssSelector : GLib.Object {
 }
 
 public class GXml.CssElementSelector : GXml.CssSelector {
-       public CssElementSelector (string prefix = "", string local_name = "", bool prefixed = false) {
-               GLib.Object (selector_type : GXml.CssSelectorType.ELEMENT, name : prefix, value : local_name, 
prefixed : prefixed);
+       public CssElementSelector (string? prefix = null, string local_name = "") {
+               base (GXml.CssSelectorType.ELEMENT);
+               this.name = prefix;
+               this.value = local_name;
        }
        
-       public bool prefixed { get; set construct; }
+       public bool prefixed {
+               get {
+                       return this.prefix != null;
+               }
+       }
        
-       public string prefix {
+       public string? prefix {
                owned get {
                        return this.name;
                }
@@ -141,6 +147,19 @@ public class GXml.CssElementSelector : GXml.CssSelector {
        }
 }
 
+public class GXml.CssAttributeSelector : GXml.CssSelector {
+       public CssAttributeSelector (string? prefix = null, string local_name = "") {
+               base (GXml.CssSelectorType.ATTRIBUTE);
+               this.prefix = prefix;
+               this.local_name = local_name;
+               this.name = (prefix == null) ? local_name : "%s|%s".printf (prefix, local_name);
+       }
+       
+       public string? prefix { get; set; }
+       
+       public string local_name { get; set; }
+}
+
 public class GXml.CssNotSelector : GXml.CssSelector {
        public CssNotSelector() {
                GLib.Object (selector_type : GXml.CssSelectorType.PSEUDO_CLASS, name : "not");
@@ -166,7 +185,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                        '$', '&', '#', '|', '`',
                        '^', '@', '+', '~', '*',
                        '%', '!', '?', '<', '>', 
-                       '.', '"', '\''
+                       ':', '.', '"', '\''
                };
                return !(u in array);
        }
@@ -193,11 +212,11 @@ public class GXml.CssSelectorParser : GLib.Object {
                                throw new GXml.CssSelectorError.IDENTIFIER ("string value is empty");
                        if (extra_builder.str.contains ("*") && extra_builder.len > 1)
                                throw new GXml.CssSelectorError.IDENTIFIER ("Invalid identifier");
-                       return new GXml.CssElementSelector (builder.str, extra_builder.str, true);
+                       return new GXml.CssElementSelector (builder.str, extra_builder.str);
                }
                if (builder.len == 0)
                        throw new GXml.CssSelectorError.IDENTIFIER ("string value is empty");
-               return new GXml.CssElementSelector ("", builder.str);
+               return new GXml.CssElementSelector (null, builder.str);
        }
        
        static GXml.CssNotSelector parse_not_selector (GXml.CssString str) throws GLib.Error {
@@ -265,16 +284,36 @@ public class GXml.CssSelectorParser : GLib.Object {
                str.read();
                while (str.peek().isspace())
                        str.read();
-               if (!str.peek().isalpha())
-                       throw new GXml.CssSelectorError.ATTRIBUTE ("Attribute key doesn't start with letter");
-               var key = parse_identifier (str, s => { return !s.peek().isspace(); });
-               if (key.length == 0)
-                       throw new GXml.CssSelectorError.ATTRIBUTE ("No key found for current attribute 
selector");
+                       
+               var builder = new StringBuilder();
+               var extra_builder = new StringBuilder();
+               bool prefixed = false;
+               while (!str.eof && (str.peek() == '*' || is_valid_char (str.peek())) && !str.peek().isspace())
+                       builder.append_unichar (str.read());    
+               if (builder.str.contains ("*") && builder.len > 1)
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Invalid attribute");
+               if (str.peek() == '|') {
+                       str.read();
+                       if (str.peek() == '=')
+                               str.read_r();
+                       else {
+                               prefixed = true;
+                               while (!str.eof && (str.peek() == '*' || is_valid_char (str.peek())) && 
!str.peek().isspace())
+                                       extra_builder.append_unichar (str.read());
+                               if (extra_builder.len == 0)
+                                       throw new GXml.CssSelectorError.ATTRIBUTE ("string value is empty");
+                               if (extra_builder.str.contains ("*") && extra_builder.len > 1)
+                                       throw new GXml.CssSelectorError.ATTRIBUTE ("Invalid attribute");
+                       }
+               }
+               string? prefix = prefixed ? builder.str : null;
+               string local_name = prefixed ? extra_builder.str : builder.str;
+               var selector = new GXml.CssAttributeSelector (prefix, local_name);
                while (str.peek().isspace())
                        str.read();
                if (str.peek() == ']') {
                        str.read();
-                       return new GXml.CssSelector (GXml.CssSelectorType.ATTRIBUTE, key);
+                       return selector;
                }
                var ct = GXml.CssSelectorType.CLASS;
                if (str.peek() == '=')
@@ -291,6 +330,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                        ct = GXml.CssSelectorType.ATTRIBUTE_SUBSTRING;
                if (ct == GXml.CssSelectorType.CLASS)
                        throw new GXml.CssSelectorError.ATTRIBUTE ("Invalid attribute selector");
+               selector.selector_type = ct;
                if (ct != GXml.CssSelectorType.ATTRIBUTE_EQUAL)
                        str.read();
                if (str.peek() != '=')
@@ -301,7 +341,11 @@ public class GXml.CssSelectorParser : GLib.Object {
                unichar quote = 0;
                if (str.peek() == '"' || str.peek() == '\'')
                        quote = str.read();
-               var attr_value = parse_identifier (str, s => { return s.peek() != quote; });
+               var attr_value = parse_identifier (str, s => {
+                       return quote == 0 && !s.peek().isspace() || quote > 0 && s.peek() != quote;
+               });
+               selector.value = attr_value;
+               print ("coucou la valeur : %s\n", attr_value);
                if (quote > 0 && quote != str.read())
                        throw new GXml.CssSelectorError.ATTRIBUTE ("Cannot find end of attribute value");
                while (str.peek().isspace())
@@ -311,7 +355,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                
                // TODO : CSS Selectors level 4 : case sensivity.
                
-               return new GXml.CssSelector.with_value (ct, key, attr_value);
+               return selector;
        }
        
        static void parse_selectors (GXml.CssString str, Gee.List<GXml.CssSelector> list, unichar stop_char = 
0) throws GLib.Error {
@@ -328,7 +372,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                                list.add (parse_attribute (str));
                        if (list[list.size - 1] is GXml.CssElementSelector) {
                                var sel = list[list.size - 1] as GXml.CssElementSelector;
-                               if (sel.prefix == "" && !sel.prefixed && sel.local_name == "*")
+                               if (!sel.prefixed && sel.local_name == "*")
                                        list[list.size - 1] = new GXml.CssSelector (GXml.CssSelectorType.ALL);
                        }
                        
@@ -355,6 +399,8 @@ public class GXml.CssSelectorParser : GLib.Object {
                }
                if (list.size == 0)
                        throw new GXml.CssSelectorError.NULL ("No selectors found");
+               foreach (var sel in list)
+                       print ("%s %s %s\n", sel.selector_type.to_string(), sel.name, sel.value);
                if (list[list.size - 1].combiner == GXml.CssCombiner.NONE)
                        list[list.size - 1].combiner = GXml.CssCombiner.NULL;
                if (list[list.size - 1].combiner != GXml.CssCombiner.NULL)
@@ -531,6 +577,62 @@ public class GXml.CssSelectorParser : GLib.Object {
                }
                return false;
        }
+       
+       static bool match_attribute (GXml.DomElement element, GXml.CssAttributeSelector selector) throws 
GLib.Error {
+               var list = new Gee.ArrayList<GXml.DomNode>();
+               if (selector.prefix != null) {
+                       if (selector.prefix == "") {
+                               for (var i = 0; i < element.attributes.length; i++) {
+                                       if (element.attributes.item (i) == null)
+                                               continue;
+                                       if (element.attributes.item (i).node_name == selector.local_name || 
selector.local_name == "*")
+                                               list.add (element.attributes.item (i));
+                               }
+                       }
+                       else if (selector.prefix == "*") {
+                               for (var i = 0; i < element.attributes.length; i++) {
+                                       if (element.attributes.item (i) == null)
+                                               continue;
+                                       var attr_name = element.attributes.item (i).node_name;
+                                       if (attr_name.split (":").length == 2 && (attr_name.split (":")[1] == 
selector.local_name || selector.local_name == "*"))
+                                               list.add (element.attributes.item (i));
+                               }
+                       }
+                       else {
+                               for (var i = 0; i < element.attributes.length; i++) {
+                                       if (element.attributes.item (i) == null)
+                                               continue;
+                                       var attr_name = element.attributes.item (i).node_name;
+                                       if (attr_name.split (":").length == 2 && attr_name.split (":")[0] == 
selector.prefix && (attr_name.split (":")[1] == selector.local_name || selector.local_name == "*"))
+                                               list.add (element.attributes.item (i));
+                               }
+                       }
+               }
+               else {
+                       var attr = element.attributes.get_named_item (selector.local_name);
+                       if (attr != null)
+                               list.add (attr);
+               }
+               if (list.size == 0)
+                       return false;
+               foreach (var attr in list) {
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE)
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_EQUAL && attr.node_value 
== selector.value)
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_CONTAINS && 
attr.node_value != null && (selector.value in attr.node_value.split (" ")))
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_SUBSTRING && 
attr.node_value != null && attr.node_value.contains (selector.value))
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH_WORD && 
attr.node_value != null && attr.node_value.split ("-")[0] == selector.value)
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH && 
attr.node_value != null && attr.node_value.index_of (selector.value) == 0)
+                               return true;
+                       if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_ENDS_WITH && 
attr.node_value != null && attr.node_value.last_index_of (selector.value) == attr.node_value.length - 
selector.value.length)
+                               return true;
+               }
+               return false;
+       }
                        
        static bool match_node (GXml.DomElement element, GXml.CssSelector selector) throws GLib.Error {
                if (selector.selector_type == GXml.CssSelectorType.ALL)
@@ -539,6 +641,8 @@ public class GXml.CssSelectorParser : GLib.Object {
                        return true;
                if (selector.selector_type == GXml.CssSelectorType.ID && element.id == selector.name)
                        return true;
+               if (selector is GXml.CssAttributeSelector)
+                       return match_attribute (element, selector as GXml.CssAttributeSelector);
                if (selector is GXml.CssElementSelector) {
                        var sel = selector as GXml.CssElementSelector;
                        if (sel.local_name == "*" && (!sel.prefixed || sel.prefix == "*"))
@@ -552,6 +656,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                                return sel.local_name == element.local_name;
                        return sel.local_name == element.local_name && sel.prefix == element.prefix;
                }
+               /*
                if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE && 
element.attributes.get_named_item (selector.name) != null)
                        return true;
                if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_EQUAL && element.get_attribute 
(selector.name) == selector.value)
@@ -566,6 +671,7 @@ public class GXml.CssSelectorParser : GLib.Object {
                        return true;
                if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_ENDS_WITH && 
element.get_attribute (selector.name) != null && element.get_attribute (selector.name).last_index_of 
(selector.value) == element.get_attribute (selector.name).length - selector.value.length)
                        return true;
+               */
                if (selector is GXml.CssNotSelector)
                        return !match_element (element, (selector as GXml.CssNotSelector).selectors);
                if (selector.selector_type == GXml.CssSelectorType.PSEUDO_CLASS)
diff --git a/test/CssSelectorTest.vala b/test/CssSelectorTest.vala
index 8bb08e5..681fa79 100644
--- a/test/CssSelectorTest.vala
+++ b/test/CssSelectorTest.vala
@@ -198,7 +198,7 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name$=\"val\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
@@ -367,7 +367,7 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("toplevel:root");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]