[gxml] GomElement: re-implemented attributes using GomAttr



commit 791f27dfd245b56ffe63a5a765ee40d23f47f4ff
Author: Daniel Espinosa <esodan gmail com>
Date:   Wed Mar 27 17:35:05 2019 -0600

    GomElement: re-implemented attributes using GomAttr
    
    GomAttr now can hold references and update GomObject
    attributes, so object's properties are updated too,
    while a list of all attributes is available

 gxml/GomAttr.vala        | 37 ++++++++++++++++---
 gxml/GomElement.vala     | 93 +++++++++++++++++++++---------------------------
 gxml/GomObject.vala      | 14 ++++++--
 gxml/XParser.vala        |  4 +--
 test/GomElementTest.vala | 89 ++++++++++++++++++++++++++++++++++++---------
 5 files changed, 157 insertions(+), 80 deletions(-)
---
diff --git a/gxml/GomAttr.vala b/gxml/GomAttr.vala
index bc19273..734372e 100644
--- a/gxml/GomAttr.vala
+++ b/gxml/GomAttr.vala
@@ -28,6 +28,7 @@ using Gee;
  */
 public class GXml.GomAttr : GXml.GomNode, GXml.DomAttr {
   protected string _namespace_uri;
+  protected GomProperty prop = null;
   public string local_name { owned get { return _local_name; } }
   public string name {
     owned get {
@@ -43,22 +44,48 @@ public class GXml.GomAttr : GXml.GomNode, GXml.DomAttr {
       return _prefix;
     }
   }
-  public string value { owned get { return _node_value; } set { _node_value = value; } }
+  public string value {
+    owned get {
+      if (prop != null) {
+        return prop.value;
+      }
+      return _node_value;
+    }
+    set {
+      if (prop != null) {
+        prop.value = value;
+      } else {
+        _node_value = value;
+      }
+    }
+  }
+
+  public bool is_referenced { get { return prop != null; } }
 
   construct { _child_nodes = null; }
 
-  public GomAttr (DomElement element, string name, string value) {
+  public GomAttr (DomElement element, string name, string val) {
     _document = element.owner_document;
     _parent = element;
     _local_name = name;
-    _node_value = value;
+    _node_value = val;
+    assert (node_value == val);
+    prop = null;
   }
-  public GomAttr.namespace (DomElement element, string namespace_uri, string? prefix, string name, string 
value) {
+  public GomAttr.namespace (DomElement element, string namespace_uri, string? prefix, string name, string 
val) {
     _document = element.owner_document;
     _parent = element;
     _local_name = name;
-    _node_value = value;
+    _node_value = val;
     _namespace_uri = namespace_uri;
     _prefix = prefix;
+    prop = null;
+  }
+  public GomAttr.reference (DomElement element, string name) {
+    _document = element.owner_document;
+    _parent = element;
+    _local_name = name;
+    _node_value = null;
+    prop = new GomStringRef (element as GomObject, name);
   }
 }
diff --git a/gxml/GomElement.vala b/gxml/GomElement.vala
index 9d084c8..ec8d4a2 100644
--- a/gxml/GomElement.vala
+++ b/gxml/GomElement.vala
@@ -161,7 +161,7 @@ public class GXml.GomElement : GomNode,
     foreach (string k in _attributes.keys) {
       if (!("xmlns" in k)) continue;
       string ns_uri = null;
-      var prop = _attributes.get (k);
+      var prop = _attributes.get (k) as DomAttr;
       if (prop != null) {
           ns_uri = prop.value;
       }
@@ -186,7 +186,7 @@ public class GXml.GomElement : GomNode,
   public new string? lookup_namespace_uri (string? prefix) {
     foreach (string k in attributes.keys) {
       if (!("xmlns" in k)) continue;
-      var p = _attributes.get (k);
+      var p = _attributes.get (k) as DomAttr;
       string nsp = null;
       if (p != null) {
         nsp = p.value;
@@ -315,7 +315,7 @@ public class GXml.GomElement : GomNode,
         var p = _attributes.get (name.down ());
         if (p == null) {
           GomProperty prop = new GomStringRef (this, name);
-          _attributes.add (name, prop);
+          _attributes.add_reference (name);
         }
       }
     });
@@ -370,13 +370,15 @@ public class GXml.GomElement : GomNode,
    * and it's value as value. Appends namespace prefix to attribute's name as
    * key if is a namespaced attribute.
    */
-  public class Attributes : HashMap<string,GomProperty>, DomNamedNodeMap  {
+  public class Attributes : HashMap<string,DomNode>, DomNamedNodeMap  {
     private TreeMap<long,string> order = new TreeMap<long,string> ();
     /**
      * Holds {@link GomElement} refrence to attributes' parent element.
      * Derived classes should not modify, but set at construction time.
      */
     protected GomElement _element;
+
+    // DomNamedNodeMap
     public int length { get { return size; } }
     public DomNode? item (int index) {
       if (index < 0 || index >= size) return null;
@@ -384,13 +386,9 @@ public class GXml.GomElement : GomNode,
       foreach (Gee.Map.Entry<long,string> e in order.ascending_entries) {
         i++;
         if (i == index) {
-          string name = e.value;
-          string v = null;
-          var o = get (name);
-          if (o != null) {
-              v = o.value;
-          }
-          return new GomAttr (_element, name, v);
+          var ret = get (e.value);
+          message ("Found %s at %ld - Val=%s - as node val = %s", e.value, i, (ret as DomAttr).value, 
ret.node_value);
+          return ret;
         }
       }
       return null;
@@ -421,7 +419,7 @@ public class GXml.GomElement : GomNode,
         if (p != "xmlns" && p != "xml")
           ns =  _element.lookup_namespace_uri (p);
       }
-      var prop = get (n);
+      var prop = get (n) as DomAttr;
       string val = null;
       if (prop != null) {
           val = prop.value;
@@ -446,45 +444,42 @@ public class GXml.GomElement : GomNode,
         throw new DomError.INVALID_CHARACTER_ERROR (_("Invalid attribute name: %s"), (node as 
DomAttr).local_name);
       if (!(node is DomAttr))
         throw new DomError.HIERARCHY_REQUEST_ERROR (_("Invalid node type. DomAttr was expected"));
-      GomProperty prop = null;
+      GomAttr attr = null;
       var pprop = (_element as GomObject).find_property_name ((node as DomAttr).local_name);
       if (pprop != null) {
         (_element as GomObject).set_attribute ((node as DomAttr).local_name, node.node_value);
-        prop = new GomStringRef (_element, (node as DomAttr).local_name);
+        attr = new GomAttr.reference (_element, (node as DomAttr).local_name);
       } else {
-        prop = new GomString.with_string (node.node_value);
+        attr = new GomAttr (_element, (node as DomAttr).local_name, node.node_value);
       }
-      set ((node as DomAttr).local_name.down (), prop);
-      order.set (size, (node as DomAttr).local_name.down ());
-      return new GomAttr (_element, (node as DomAttr).local_name, node.node_value);
+      set (attr.local_name.down (), attr);
+      order.set (size - 1, (node as DomAttr).local_name.down ());
+      return attr;
     }
     public DomNode? remove_named_item (string name) throws GLib.Error {
       if (":" in name) return null;
       string val = null;
-      var prop = get (name.down ());
+      var prop = get (name.down ()) as DomAttr;
       if (prop != null) {
-          val = prop.value;
-          prop.value = null;
-      }
-      var n = new GomAttr (_element, name, val);
-      long i = index_of (name);
-      unset (name.down ());
-      if (i < 0) {
-        warning (_("No index found for attribute %s").printf (name));
-      } else {
-        order.unset (i);
+        val = prop.value;
+        prop.value = null;
+        long i = index_of (name);
+        unset (name.down ());
+        if (i < 0) {
+          warning (_("No index found for attribute %s").printf (name));
+        } else {
+          order.unset (i);
+        }
       }
-      return n;
+      return prop as DomNode;
     }
     // Introduced in DOM Level 2:
     public DomNode? remove_named_item_ns (string namespace_uri, string local_name) throws GLib.Error {
       if (":" in local_name) return null;
       var nsp = _element.lookup_prefix (namespace_uri);
       if (nsp == null || nsp == "") return null;
-      var v = get ((nsp+":"+local_name).down ());
+      var v = get ((nsp+":"+local_name).down ()) as DomAttr;
       if (v == null) return null;
-      string val = v.value;
-      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, val);
       string k = (nsp+":"+local_name).down ();
       v.value = null;
       unset (k);
@@ -494,7 +489,7 @@ public class GXml.GomElement : GomNode,
       } else {
         order.unset (i);
       }
-      return n;
+      return v as DomNode;
     }
     // Introduced in DOM Level 2:
     public DomNode? get_named_item_ns (string namespace_uri, string local_name) throws GLib.Error {
@@ -502,10 +497,7 @@ public class GXml.GomElement : GomNode,
       var nsp = _element.lookup_prefix (namespace_uri);
       if (nsp == null) return null;
       var v = get ((nsp+":"+local_name).down ());
-      if (v == null) return null;
-      string val = v.value;
-      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, val);
-      return n;
+      return v;
     }
     // Introduced in DOM Level 2:
     public DomNode? set_named_item_ns (DomNode node) throws GLib.Error {
@@ -574,22 +566,16 @@ public class GXml.GomElement : GomNode,
           && (node as DomAttr).prefix != "")
         p = (node as DomAttr).prefix + ":";
       string k = (p+(node as DomAttr).local_name).down ();
-      GomProperty prop = null;
+      GomAttr attr = null;
       var pprop = (_element as GomObject).find_property_name ((node as DomAttr).local_name);
       if (pprop != null) {
         (_element as GomObject).set_attribute ((node as DomAttr).local_name, node.node_value);
-        prop = new GomStringRef (_element, (node as DomAttr).local_name);
+        attr = new GomAttr.reference (_element, (node as DomAttr).local_name);
       } else {
-        prop = new GomString.with_string (node.node_value);
+        attr = new GomAttr.namespace (_element, (node as DomAttr).namespace_uri, (node as DomAttr).prefix, 
(node as DomAttr).local_name, node.node_value);
       }
-      set (k, prop);
-      order.set (size, k);
-
-      var attr = new GomAttr.namespace (_element,
-                                    (node as DomAttr).namespace_uri,
-                                    (node as DomAttr).prefix,
-                                    (node as DomAttr).local_name,
-                                    node.node_value);
+      set (k, attr);
+      order.set (size - 1, k);
       return attr;
     }
     private long index_of (string name) {
@@ -600,15 +586,16 @@ public class GXml.GomElement : GomNode,
       }
       return -1;
     }
-    public void add (string name, GomProperty prop) {
-      set (name.down (), prop);
+    public void add_reference (string name) {
+      var attr = new GomAttr.reference (_element, name);
+      set (name.down (), attr);
       order.set (size, name);
     }
   }
   public DomNamedNodeMap attributes { owned get { return (DomNamedNodeMap) _attributes; } }
   public string? get_attribute (string name) {
     string str = null;
-    var prop = _attributes.get (name.down ());
+    var prop = _attributes.get (name.down ()) as DomAttr;
     if (prop != null) {
         str = prop.value;
     }
@@ -626,7 +613,7 @@ public class GXml.GomElement : GomNode,
     if (nsp != null)
       name = nsp + ":" + local_name;
     string val = null;
-    var prop = _attributes.get (name);
+    var prop = _attributes.get (name) as DomAttr;
     if (prop != null) {
         val = prop.value;
     }
diff --git a/gxml/GomObject.vala b/gxml/GomObject.vala
index 6974a64..562c30e 100644
--- a/gxml/GomObject.vala
+++ b/gxml/GomObject.vala
@@ -342,9 +342,17 @@ public interface GXml.GomObject : GLib.Object,
   public virtual bool remove_attribute (string name) {
     var prop = get_class ().find_property (name);
     if (prop != null) {
-      Value v = Value (typeof (Object));
-      (this as Object).set_property (name, v);
-      return true;
+      if (prop.value_type.is_a (typeof (Object))) {
+        Value v = Value (typeof (Object));
+        (this as Object).set_property (name, v);
+        return true;
+      }
+      if (prop.value_type.is_a (typeof (string))) {
+        Value v = Value (typeof (string));
+        v.set_string (null);
+        set_property (prop.name, v);
+        return true;
+      }
     }
     return false;
   }
diff --git a/gxml/XParser.vala b/gxml/XParser.vala
index 08212af..fdb7931 100644
--- a/gxml/XParser.vala
+++ b/gxml/XParser.vala
@@ -513,11 +513,11 @@ public class GXml.XParser : Object, GXml.Parser {
     // DomElement attributes
     var keys = (node as DomElement).attributes.keys;
     foreach (string ak in keys) {
-      var prop = ((node as DomElement).attributes as HashMap<string,GomProperty>).get (ak);
+      var prop = (node as DomElement).attributes.get (ak) as GomAttr;
       if (prop == null) {
         continue;
       }
-      if (prop is GomStringRef) {
+      if (prop.is_referenced) {
         continue;
       }
       string v = prop.value;
diff --git a/test/GomElementTest.vala b/test/GomElementTest.vala
index a7ff1fb..1678188 100644
--- a/test/GomElementTest.vala
+++ b/test/GomElementTest.vala
@@ -36,6 +36,12 @@ class ObjectParent : GomElement {
        public string text { get; set; }
        [Description (nick="::prop")]
        public ObjectProperty prop { get; set; }
+       [Description (nick="::prop1")]
+       public ObjectProperty prop1 { get; set; }
+       [Description (nick="::prop2")]
+       public ObjectProperty prop2 { get; set; }
+       [Description (nick="::prop3")]
+       public ObjectProperty prop3 { get; set; }
        public class ObjectProperty : Object, GomProperty {
                public string? value { owned get; set; }
                public bool validate_value (string? val) {
@@ -748,7 +754,7 @@ class GomElementTest : GXmlTest  {
                    assert_not_reached ();
                  }
                });
-               Test.add_func ("/gxml/gom-element/object-attributes", () => {
+               Test.add_func ("/gxml/gom-element/object-attributes/attributes-collection", () => {
                        try {
                                var e = new ObjectParent ();
                                assert (e.text == null);
@@ -763,37 +769,42 @@ class GomElementTest : GXmlTest  {
                                assert (e.text != null);
                                assert (e.prop != null);
                                assert (e.attributes.length == 2);
-                               assert (e.attributes.item (0).node_value == "value1");
-                               assert (e.attributes.item (1).node_value == "value_prop");
+                               message ("Attr 0: %s:%s", e.attributes.item (0).node_name, e.attributes.item 
(0).node_value);
+                               foreach (string k in e.attributes.keys) {
+                                       var a = e.attributes.get (k) as DomAttr;
+                                       message ("Attr: %s:%s", a.name, a.@value);
+                               }
+                               assert ((e.attributes.item (0) as DomAttr).@value == "value1");
+                               assert ((e.attributes.item (1) as DomAttr).@value == "value_prop");
                                e.set_attribute ("p1", "prop1");
                                e.set_attribute ("p2", "prop2");
                                e.set_attribute ("p3", "prop3");
                                assert (e.attributes.length == 5);
-                               assert (e.attributes.item (0).node_value == "value1");
-                               assert (e.attributes.item (1).node_value == "value_prop");
-                               assert (e.attributes.item (2).node_value == "prop1");
-                               assert (e.attributes.item (3).node_value == "prop2");
-                               assert (e.attributes.item (4).node_value == "prop3");
+                               assert ((e.attributes.item (0) as DomAttr).@value == "value1");
+                               assert ((e.attributes.item (1) as DomAttr).@value == "value_prop");
+                               assert ((e.attributes.item (2) as DomAttr).@value == "prop1");
+                               assert ((e.attributes.item (3) as DomAttr).@value == "prop2");
+                               assert ((e.attributes.item (4) as DomAttr).@value == "prop3");
                                e.set_attribute_ns ("http://www.w3.org/2000/xmlns/";, "xmlns:t", 
"http://www.gnome.org/gxml/test";);
                                e.set_attribute_ns ("http://www.gnome.org/gxml/test";, "t:p1", "prop1_test");
                                e.set_attribute_ns ("http://www.gnome.org/gxml/test";, "t:p2", "prop2_test");
                                assert (e.get_attribute_ns ("http://www.gnome.org/gxml/test";, "p1") == 
"prop1_test");
                                assert (e.get_attribute_ns ("http://www.gnome.org/gxml/test";, "p2") == 
"prop2_test");
                                assert (e.attributes.length == 8);
-                               assert (e.attributes.item (0).node_value == "value1");
-                               assert (e.attributes.item (1).node_value == "value_prop");
-                               assert (e.attributes.item (2).node_value == "prop1");
-                               assert (e.attributes.item (3).node_value == "prop2");
-                               assert (e.attributes.item (4).node_value == "prop3");
-                               assert (e.attributes.item (5).node_value == "http://www.gnome.org/gxml/test";);
-                               assert (e.attributes.item (6).node_value == "prop1_test");
-                               assert (e.attributes.item (7).node_value == "prop2_test");
+                               assert ((e.attributes.item (0) as DomAttr).@value == "value1");
+                               assert ((e.attributes.item (1) as DomAttr).@value == "value_prop");
+                               assert ((e.attributes.item (2) as DomAttr).@value == "prop1");
+                               assert ((e.attributes.item (3) as DomAttr).@value == "prop2");
+                               assert ((e.attributes.item (4) as DomAttr).@value == "prop3");
+                               assert ((e.attributes.item (5) as DomAttr).@value == 
"http://www.gnome.org/gxml/test";);
+                               assert ((e.attributes.item (6) as DomAttr).@value == "prop1_test");
+                               assert ((e.attributes.item (7) as DomAttr).@value == "prop2_test");
                                e.id = "di1";
                                assert (e.id == "di1");
                                assert (e.get_attribute ("id") == "di1");
                                assert (e.attributes.length == 9);
                                assert (e.attributes.item (8) != null);
-                               assert (e.attributes.item (8).node_value == "di1");
+                               assert ((e.attributes.item (8) as DomAttr).@value == "di1");
                                e.child = Object.new (typeof (ObjectParent.ObjectChild),
                                                                                                              
          "owner-document", e.owner_document) as ObjectParent.ObjectChild;
                                e.append_child (e.child);
@@ -803,6 +814,50 @@ class GomElementTest : GXmlTest  {
                                e2.read_from_string (e.write_string ());
                                message (e.write_string ());
                                assert (e2.child != null);
+                               // Check attributes collection structure
+                               assert (e.attributes is DomNamedNodeMap);
+                               foreach (string k in e.attributes.keys) {
+                                       var item = e.attributes.get (k) as DomAttr;
+                                       assert (item != null);
+                               }
+                       } catch (GLib.Error e) {
+                   GLib.message ("Error: "+e.message);
+                   assert_not_reached ();
+                 }
+               });
+               Test.add_func ("/gxml/gom-element/object-attributes/attributes-update", () => {
+                       try {
+                               var e = new ObjectParent ();
+                               e.id = "id1";
+                               assert (e.get_attribute ("id") == "id1");
+                               e.set_attribute ("id", "id2");
+                               assert (e.get_attribute ("id") == "id2");
+                               assert (e.id == "id2");
+                               assert ((e.attributes.item (0) as DomAttr).value == "id2");
+                               e.set_attribute ("prop", "val_prop");
+                               assert (e.prop != null);
+                               assert (e.prop is ObjectParent.ObjectProperty);
+                               assert (e.prop.value == "val_prop");
+                               assert (e.get_attribute ("prop") == "val_prop");
+                               assert ((e.attributes.item (1) as DomAttr).value == "val_prop");
+                               e.set_attribute ("prop1", "val_prop1");
+                               assert (e.prop1 != null);
+                               assert (e.prop1 is ObjectParent.ObjectProperty);
+                               assert (e.prop1.value == "val_prop1");
+                               assert (e.get_attribute ("prop1") == "val_prop1");
+                               assert ((e.attributes.item (2) as DomAttr).value == "val_prop1");
+                               e.set_attribute ("prop2", "val_prop2");
+                               assert (e.prop2 != null);
+                               assert (e.prop2 is ObjectParent.ObjectProperty);
+                               assert (e.prop2.value == "val_prop2");
+                               assert (e.get_attribute ("prop2") == "val_prop2");
+                               assert ((e.attributes.item (3) as DomAttr).value == "val_prop2");
+                               e.set_attribute ("prop3", "val_prop3");
+                               assert (e.prop3 != null);
+                               assert (e.prop3 is ObjectParent.ObjectProperty);
+                               assert (e.prop3.value == "val_prop3");
+                               assert (e.get_attribute ("prop3") == "val_prop3");
+                               assert ((e.attributes.item (4) as DomAttr).value == "val_prop3");
                        } catch (GLib.Error e) {
                    GLib.message ("Error: "+e.message);
                    assert_not_reached ();


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