[gxml] GomElement: Object properties now are included in attributes



commit 5995bb232690327453a733f241a2591858939b37
Author: Daniel Espinosa <esodan gmail com>
Date:   Tue Mar 19 17:42:19 2019 -0600

    GomElement: Object properties now are included in attributes
    
    Fix https://gitlab.gnome.org/GNOME/gxml/issues/19

 gxml/GomElement.vala     | 105 +++++++++++++++++++++++++++++++++++------------
 gxml/GomObject.vala      |  23 +++++++++++
 gxml/GomProperty.vala    |   3 ++
 gxml/GomStringRef.vala   |  39 ++++++++++++++++++
 gxml/meson.build         |   1 +
 test/GomElementTest.vala |  28 +++++++++++++
 6 files changed, 173 insertions(+), 26 deletions(-)
---
diff --git a/gxml/GomElement.vala b/gxml/GomElement.vala
index 367db46..5c91216 100644
--- a/gxml/GomElement.vala
+++ b/gxml/GomElement.vala
@@ -164,7 +164,11 @@ public class GXml.GomElement : GomNode,
       return _prefix;
     foreach (string k in _attributes.keys) {
       if (!("xmlns" in k)) continue;
-      string ns_uri = _attributes.get (k);
+      string ns_uri = null;
+      var prop = _attributes.get (k);
+      if (prop != null) {
+          ns_uri = prop.value;
+      }
       if (ns_uri == null) {
         GLib.warning (_("Invalid namespace URI stored in element's attribute"));
         return null;
@@ -186,7 +190,11 @@ public class GXml.GomElement : GomNode,
   public new string? lookup_namespace_uri (string? prefix) {
     foreach (string k in attributes.keys) {
       if (!("xmlns" in k)) continue;
-      string nsp = _attributes.get (k);
+      var p = _attributes.get (k);
+      string nsp = null;
+      if (p != null) {
+        nsp = p.value;
+      }
       if (prefix == null && k == "xmlns") return nsp;
       if (":" in k) {
         string[] sa = k.split (":");
@@ -351,13 +359,12 @@ public class GXml.GomElement : GomNode,
     _namespace_uri = namespace_uri;
     _prefix = prefix;
   }
-
   /**
    * Holds attributes in current node, using attribute's name as key
    * and it's value as value. Appends namespace prefix to attribute's name as
-   * key if a namespaced attribute.
+   * key if is a namespaced attribute.
    */
-  public class Attributes : HashMap<string,string>, DomNamedNodeMap  {
+  public class Attributes : HashMap<string,GomProperty>, DomNamedNodeMap  {
     private TreeMap<long,string> order = new TreeMap<long,string> ();
     /**
      * Holds {@link GomElement} refrence to attributes' parent element.
@@ -372,7 +379,11 @@ public class GXml.GomElement : GomNode,
         i++;
         if (i == index) {
           string name = e.value;
-          string v = get (name);
+          string v = null;
+          var o = get (name);
+          if (o != null) {
+              v = o.value;
+          }
           return new GomAttr (_element, name, v);
         }
       }
@@ -404,7 +415,11 @@ public class GXml.GomElement : GomNode,
         if (p != "xmlns" && p != "xml")
           ns =  _element.lookup_namespace_uri (p);
       }
-      string val = get (name);
+      var prop = get (name);
+      string val = null;
+      if (prop != null) {
+          val = prop.value;
+      }
       if (val == null) return null;
       DomNode attr = null;
       if (p == null || p == "")
@@ -425,15 +440,27 @@ 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"));
-      set ((node as DomAttr).local_name, node.node_value);
+      GomProperty prop = 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);
+      } else {
+        prop = new GomString.with_string (node.node_value);
+      }
+      set ((node as DomAttr).local_name, prop);
       order.set (size, (node as DomAttr).local_name);
       return new GomAttr (_element, (node as DomAttr).local_name, node.node_value);
     }
     public DomNode? remove_named_item (string name) throws GLib.Error {
       if (":" in name) return null;
-      var v = get (name);
-      if (v == null) return null;
-      var n = new GomAttr (_element, name, v);
+      string val = null;
+      var prop = get (name);
+      if (prop != null) {
+          val = prop.value;
+          prop.value = null;
+      }
+      var n = new GomAttr (_element, name, val);
       unset (name);
       long i = index_of (name);
       if (i < 0) {
@@ -450,8 +477,10 @@ public class GXml.GomElement : GomNode,
       if (nsp == null || nsp == "") return null;
       var v = get (nsp+":"+local_name);
       if (v == null) return null;
-      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, v);
+      string val = v.value;
+      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, val);
       string k = nsp+":"+local_name;
+      v.value = null;
       unset (k);
       long i = index_of (k);
       if (i < 0) {
@@ -468,7 +497,8 @@ public class GXml.GomElement : GomNode,
       if (nsp == null) return null;
       var v = get (nsp+":"+local_name);
       if (v == null) return null;
-      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, v);
+      string val = v.value;
+      var n = new GomAttr.namespace (_element, namespace_uri, nsp, local_name, val);
       return n;
     }
     // Introduced in DOM Level 2:
@@ -538,7 +568,15 @@ public class GXml.GomElement : GomNode,
           && (node as DomAttr).prefix != "")
         p = (node as DomAttr).prefix + ":";
       string k = p+(node as DomAttr).local_name;
-      set (k, node.node_value);
+      GomProperty prop = 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);
+      } else {
+        prop = new GomString.with_string (node.node_value);
+      }
+      set (k, prop);
       order.set (size, k);
 
       var attr = new GomAttr.namespace (_element,
@@ -559,9 +597,12 @@ public class GXml.GomElement : GomNode,
   }
   public DomNamedNodeMap attributes { owned get { return (DomNamedNodeMap) _attributes; } }
   public string? get_attribute (string name) {
-    string s = (this as GomObject).get_attribute (name);
-    if (s != null) return s;
-    return _attributes.get (name);
+    string str = null;
+    var prop = _attributes.get (name);
+    if (prop != null) {
+        str = prop.value;
+    }
+    return str;
   }
   public string? get_attribute_ns (string? namespace_uri, string local_name) {
     string nsp = null;
@@ -574,16 +615,20 @@ public class GXml.GomElement : GomNode,
     string name = local_name;
     if (nsp != null)
       name = nsp + ":" + local_name;
-    return _attributes.get (name);
+    string val = null;
+    var prop = _attributes.get (name);
+    if (prop != null) {
+        val = prop.value;
+    }
+    return val;
   }
   public void set_attribute (string name, string value) throws GLib.Error {
-    bool res = (this as GomObject).set_attribute (name, value);
-    if (res) return;
     var a = new GomAttr (this, name, value);
     attributes.set_named_item (a);
   }
   public void set_attribute_ns (string? namespace_uri,
-                                string name, string value) throws GLib.Error {
+                                string name, string value) throws GLib.Error
+  {
     string p = "";
     string n = name;
     if (":" in name) {
@@ -594,8 +639,9 @@ public class GXml.GomElement : GomNode,
       n = s[1];
       if (":" in n)
         throw new DomError.NAMESPACE_ERROR (_("Invalid attribute name. Invalid use of colon: %s"), n);
-    } else
+    } else {
       n = name;
+    }
     if (namespace_uri == null && p == "")
        throw new DomError.NAMESPACE_ERROR (_("Invalid namespace. If prefix is null, namespace URI should not 
be null"));
     if (p == "xml" && namespace_uri != "http://www.w3.org/2000/xmlns/"; && namespace_uri != 
"http://www.w3.org/2000/xmlns";)
@@ -616,7 +662,6 @@ public class GXml.GomElement : GomNode,
     }
   }
   public void remove_attribute (string name) {
-    if ((this as GomObject).remove_attribute (name)) return;
     try { attributes.remove_named_item (name); }
     catch (GLib.Error e)
       { warning (_("Removing attribute Error: ")+e.message); }
@@ -630,9 +675,17 @@ public class GXml.GomElement : GomNode,
     return _attributes.has_key (name);
   }
   public bool has_attribute_ns (string? namespace_uri, string local_name) {
-    var p = lookup_prefix (namespace_uri);
-    if (p == null) return false;
-    return attributes.has_key (p+":"+local_name);
+  string nsp = null;
+    if ((namespace_uri == "http://www.w3.org/2000/xmlns/";
+          || namespace_uri == "http://www.w3.org/2000/xmlns";)
+        && local_name != "xmlns")
+      nsp = "xmlns";
+    else
+      nsp = lookup_prefix (namespace_uri);
+    string name = local_name;
+    if (nsp != null)
+      name = nsp + ":" + local_name;
+    return attributes.has_key (name);
   }
 
 
diff --git a/gxml/GomObject.vala b/gxml/GomObject.vala
index dcfe3de..4247e89 100644
--- a/gxml/GomObject.vala
+++ b/gxml/GomObject.vala
@@ -188,6 +188,29 @@ public interface GXml.GomObject : GLib.Object,
     if (prop == null) return null;
     return get_property_string (prop);
   }
+  /**
+   * Search for a property of type {@link GomProperty}
+   * and returns it as object
+   */
+  public virtual GomProperty? find_property (string name) {
+    var prop = find_property_name (name);
+    if (prop != null) {
+      var v = Value (prop.value_type);
+      if (prop.value_type.is_a (typeof(GomProperty))
+          && prop.value_type.is_instantiatable ()) {
+        get_property (prop.name, ref v);
+        GomProperty so = (Object) v as GomProperty;
+        if (so == null) {
+          var obj = Object.new (prop.value_type);
+          v.set_object (obj);
+          set_property (prop.name, v);
+          so = obj as GomProperty;
+        }
+        return so;
+      }
+    }
+    return null;
+  }
   /**
    * Search for a {@link GLib.Object} property with
    * given name, if found, given string representation
diff --git a/gxml/GomProperty.vala b/gxml/GomProperty.vala
index b999391..ccd1bd9 100644
--- a/gxml/GomProperty.vala
+++ b/gxml/GomProperty.vala
@@ -70,6 +70,9 @@ public class GXml.GomString : GomBaseProperty {
         _value = value;
     }
   }
+  public GomString.with_string (string str) {
+    _value = str;
+  }
 }
 
 /**
diff --git a/gxml/GomStringRef.vala b/gxml/GomStringRef.vala
new file mode 100644
index 0000000..d890de4
--- /dev/null
+++ b/gxml/GomStringRef.vala
@@ -0,0 +1,39 @@
+/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
+/*
+ * AttributeStringRef.vala
+ *
+ * Copyright (C) 2019  Daniel Espinosa <esodan gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Daniel Espinosa <esodan gmail com>
+ */
+public class GXml.GomStringRef : Object, GomProperty {
+  GomObject object;
+  string name;
+  public string? value {
+    owned get {
+        return object.get_attribute (name);
+    }
+    set {
+        object.set_attribute (name, value);
+    }
+  }
+  public bool validate_value (string val) { return true; }
+  public GomStringRef (GomObject obj, string name)  {
+      object = obj;
+      this.name = name;
+  }
+}
diff --git a/gxml/meson.build b/gxml/meson.build
index cd16e8d..e59d8a1 100644
--- a/gxml/meson.build
+++ b/gxml/meson.build
@@ -69,6 +69,7 @@ valasources = files ([
        'GomObject.vala',
        'GomProperty.vala',
        'GomSchema.vala',
+       'GomStringRef.vala',
        'GomText.vala',
        'GXmlAttribute.vala',
        'GXmlCDATA.vala',
diff --git a/test/GomElementTest.vala b/test/GomElementTest.vala
index 355962e..58a1e55 100644
--- a/test/GomElementTest.vala
+++ b/test/GomElementTest.vala
@@ -746,6 +746,8 @@ class GomElementTest : GXmlTest  {
                                var e = new ObjectParent ();
                                assert (e.text == null);
                                assert (e.prop == null);
+                               assert (e.attributes != null);
+                               assert (e.attributes.length == 0);
                                e.set_attribute ("text", "value1");
                                assert (e.get_attribute ("text") == "value1");
                                e.set_attribute ("prop", "value_prop");
@@ -753,6 +755,32 @@ class GomElementTest : GXmlTest  {
                                assert (e.get_attribute ("prop") == "value_prop");
                                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");
+                               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");
+                               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");
                        } 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]