[gxml] Added utilities for one element properties child



commit 24384905961d1b8f63645f7aff74857b73db1a7a
Author: Daniel Espinosa <esodan gmail com>
Date:   Tue Feb 21 10:08:06 2017 -0600

    Added utilities for one element properties child
    
    New GomObject.clean_property_elements() to search and remove redundant
    nodes of requested property.
    
    New GomObject.find_elements() to search and return a list of all child
    elements of same type.
    
    Added documentation on how to define properties removing redundant
    child nodes.

 gxml/GomObject.vala            |   85 ++++++++++++++++++++++++++++++-----
 gxml/GomProperty.vala          |    2 +-
 test/GomSerializationTest.vala |   95 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 160 insertions(+), 22 deletions(-)
---
diff --git a/gxml/GomObject.vala b/gxml/GomObject.vala
index f2586fd..a31bdd9 100644
--- a/gxml/GomObject.vala
+++ b/gxml/GomObject.vala
@@ -283,7 +283,7 @@ public interface GXml.GomObject : GLib.Object,
   public virtual DomElement? get_child (string name) {
     var prop = get_class ().find_property (name);
     if (prop != null) {
-      if (prop.value_type == typeof(DomElement)) {
+      if (prop.value_type.is_a (typeof(DomElement))) {
         var vo = Value(prop.value_type);
         get_property (prop.name, ref vo);
         return (DomElement) ((Object) vo);
@@ -297,19 +297,31 @@ public interface GXml.GomObject : GLib.Object,
     return null;
   }
   /**
+   * From a given property name of type {@link GomElement}, search all
+   * child nodes with node's local name equal to property.
+   */
+  public virtual DomElementList find_elements (string name) {
+    var l = new DomElementList ();
+    var prop = get_class ().find_property (name);
+    if (prop != null) {
+      if (prop.value_type.is_a (typeof(DomElement))) {
+        var o = Object.new (prop.value_type) as DomElement;
+        foreach (DomNode n in this.child_nodes) {
+          if (!(n is DomElement)) continue;
+          if ((n as DomElement).local_name.down () == o.local_name.down ())
+            l.add (n as DomElement);
+        }
+      }
+    }
+    return l;
+  }
+  /**
    * Search for a property and set it to null if possible returning true,
    * if value can't be removed or located, returns false without change.
    */
   public virtual bool remove_attribute (string name) {
     var prop = get_class ().find_property (name);
     if (prop != null) {
-      if (prop.value_type.is_a (typeof (SerializableProperty))) {
-        (this as SerializableProperty).set_serializable_property_value (null);
-        return true;
-      }
-      if (prop.value_type.is_a (typeof (SerializableCollection))) {
-        return true;
-      }
       Value v = Value (typeof (Object));
       (this as Object).set_property (name, v);
       return true;
@@ -317,9 +329,9 @@ public interface GXml.GomObject : GLib.Object,
     return false;
   }
   /**
-   * Convenient method to create an instance of given property's
+   * Convenient method to set an instance of given property's
    * name and initialize according to have same {@link DomNode.owner_document}
-   * and set its {@link DomNode.parent_node} to this.
+   * and set its {@link DomNode.parent_node} to this appending it as a child.
    * If property is a {@link GomCollection} it is initialize to use
    * this as its {@link GomCollection.element}.
    *
@@ -347,7 +359,7 @@ public interface GXml.GomObject : GLib.Object,
    *
    * Returns: true if property has been set and initialized, false otherwise.
    */
-  public bool create_instance_property (string name) {
+  public virtual bool set_instance_property (string name) {
     var prop = find_object_property_name (name);
     if (prop == null) return false;
     Value v = Value (prop.value_type);
@@ -359,7 +371,7 @@ public interface GXml.GomObject : GLib.Object,
       return true;
     }
     if (prop.value_type.is_a (typeof (GomElement))) {
-      obj = Object.new (prop.value_type,"owner_document", this.owner_document);
+      obj = Object.new (prop.value_type,"owner-document", this.owner_document);
       try { this.append_child (obj as GomElement); }
       catch (GLib.Error e) {
         warning (_("Error while attempting to instantiate property object: %s").printf (e.message));
@@ -371,4 +383,53 @@ public interface GXml.GomObject : GLib.Object,
     }
     return false;
   }
+  /**
+   * Utility method to remove all instances of a property being child elements
+   * of object. Is useful if you have a {@link GomElement} property, it should be
+   * just one child of this type and you want to overwrite it.
+   *
+   * In this example you have defined an element MyClass to be child of
+   * MyParentClass, but it should have just one element, once you set child_elements
+   * it calls {@link clean_property_elements} using property's canonicals name.
+   *
+   * {{{
+   *  public class MyClass : GomElement {
+   *    public string name { get; set; }
+   *  }
+   *  public class MyParentClass : GomElement {
+   *    private Myclass _child_elements = null;
+   *    public MyClass child_elements {
+   *      get { return _child_elements; }
+   *      set {
+   *        try {
+   *          clean_property_elements ("child-elements");
+   *          _child_elements = value;
+   *          append_child (_child_elements);
+   *        } catch (GLib.Error e) {
+   *          warning (e.message);
+   *        }
+   *      }
+   *    }
+   *  }
+   * }}}
+   *
+   * @param name property name to search value type, use canonical names.
+   *
+   * @throws DomError if property is not a {@link GomElement}.
+   */
+  public virtual
+  void clean_property_elements (string name) throws GLib.Error
+  {
+    var prop = get_class ().find_property (name);
+    if (prop != null) {
+      if (!prop.value_type.is_a (typeof (GomElement)))
+        throw new DomError.TYPE_MISMATCH_ERROR (_("Can't set value. It is not a GXmlGomElement type"));
+      var l = find_elements (name);
+      if (l.length != 0) {
+        foreach (DomElement e in l) {
+          e.remove ();
+        }
+      }
+    }
+  }
 }
diff --git a/gxml/GomProperty.vala b/gxml/GomProperty.vala
index 329c3c3..6cdea51 100644
--- a/gxml/GomProperty.vala
+++ b/gxml/GomProperty.vala
@@ -422,7 +422,7 @@ public class GXml.GomDateTime : GomBaseProperty {
       return _value.format (s);
     }
     set {
-      var tv = new TimeVal ();
+      var tv = TimeVal ();
       if (tv.from_iso8601 (value)) {
         _value = new DateTime.from_timeval_local (tv);
       } else
diff --git a/test/GomSerializationTest.vala b/test/GomSerializationTest.vala
index 3671b8f..5e12e60 100644
--- a/test/GomSerializationTest.vala
+++ b/test/GomSerializationTest.vala
@@ -210,10 +210,23 @@ class GomSerializationTest : GXmlTest  {
       FEBRUARY
     }
   }
-  public class BookRegister : GomElement {
+  public class BookRegister : GomElement, MappeableElement {
+    private Book _book = null;
     [Description (nick="::Year")]
     public int year { get; set; }
-    public Book book { get; set; }
+    public Book book {
+      get { return _book; }
+      set {
+        try {
+          clean_property_elements ("book");
+          _book = value;
+          append_child (_book);
+        } catch (GLib.Error e) {
+          warning (e.message);
+          assert_not_reached ();
+        }
+      }
+    }
     construct {
       try { initialize ("BookRegister"); }
       catch { assert_not_reached (); }
@@ -221,6 +234,15 @@ class GomSerializationTest : GXmlTest  {
     public BookRegister.document (DomDocument doc) {
       _document = doc;
     }
+    public string get_map_key () {
+      return "%d".printf (year)+"-"+book.name;
+    }
+    public Book create_book (string name) {
+      return Object.new (typeof (Book),
+                        "owner-document", this.owner_document,
+                        "name", name)
+                        as Book;
+    }
     public string to_string () {
       var parser = new XParser (this);
       string s = "";
@@ -234,6 +256,7 @@ class GomSerializationTest : GXmlTest  {
     }
   }
   public class BookStand : GomElement {
+    HashRegisters _hashmap_registers = null;
     [Description (nick="::Classification")]
     public string classification { get; set; default = "Science"; }
     public Dimension dimension_x { get; set; }
@@ -242,10 +265,26 @@ class GomSerializationTest : GXmlTest  {
     [Description (nick="DimensionZ")]
     public DimensionZ dimension_z { get; set; }
     public Registers registers { get; set; }
+    public HashRegisters hashmap_registers {
+      get {
+        if (_hashmap_registers == null)
+          _hashmap_registers = Object.new (typeof (HashRegisters),"element",this)
+                              as HashRegisters;
+        return _hashmap_registers;
+      }
+      set {
+        _hashmap_registers = value;
+      }
+    }
     public Books books { get; set; }
     construct {
       try { initialize ("BookStand"); } catch { assert_not_reached (); }
     }
+    public BookRegister create_register () {
+      return Object.new (typeof (BookRegister),
+                          "element", this)
+                        as BookRegister;
+    }
     public string to_string () {
       var parser = new XParser (this);
       string s = "";
@@ -284,6 +323,12 @@ class GomSerializationTest : GXmlTest  {
       catch { assert_not_reached (); }
     }
   }
+  public class HashRegisters : GomHashMap {
+    construct {
+      try { initialize (typeof (BookRegister)); }
+      catch { assert_not_reached (); }
+    }
+  }
   public class Books : GomHashMap {
     construct {
       try { initialize_with_key (typeof (Book), "Name"); }
@@ -530,7 +575,7 @@ class GomSerializationTest : GXmlTest  {
       assert ("<BookStand Classification=\"Science\"/>" in s);
       assert (bs.owner_document != null);
       assert (bs.registers == null);
-      assert (bs.create_instance_property ("registers"));
+      assert (bs.set_instance_property ("registers"));
       s = bs.to_string ();
       assert (s != null);
 #if DEBUG
@@ -567,7 +612,7 @@ class GomSerializationTest : GXmlTest  {
       assert ((bs.registers.get_item (0) as BookRegister).year == 2016);
       assert ((bs.registers.get_item (1) as BookRegister).year == 2010);
       assert ((bs.registers.get_item (2) as BookRegister).year == 2000);
-      assert (bs.create_instance_property("Dimension-X"));
+      assert (bs.set_instance_property("Dimension-X"));
       assert (bs.dimension_x != null);
       assert (bs.dimension_x.length == 1.0);
       s = bs.to_string ();
@@ -576,7 +621,7 @@ class GomSerializationTest : GXmlTest  {
       GLib.message ("DOC:"+s);
 //#endif
       assert ("<BookStand Classification=\"Science\"><BookRegister Year=\"2016\"/><BookRegister 
Year=\"2010\"/><Test/><BookRegister Year=\"2000\"/><Dimension Length=\"1\" Type=\"x\"/></BookStand>" in s);
-      assert (bs.create_instance_property("DimensionY"));
+      assert (bs.set_instance_property("DimensionY"));
       assert (bs.dimension_y != null);
       assert (bs.dimension_y.length == 1.0);
       s = bs.to_string ();
@@ -585,7 +630,7 @@ class GomSerializationTest : GXmlTest  {
       GLib.message ("DOC:"+s);
 //#endif
       assert ("<BookStand Classification=\"Science\"><BookRegister Year=\"2016\"/><BookRegister 
Year=\"2010\"/><Test/><BookRegister Year=\"2000\"/><Dimension Length=\"1\" Type=\"x\"/><Dimension 
Length=\"1\" Type=\"y\"/></BookStand>" in s);
-      assert (bs.create_instance_property("::DimensionZ"));
+      assert (bs.set_instance_property("::DimensionZ"));
       assert (bs.dimension_z != null);
       assert (bs.dimension_z.length == 1.0);
       s = bs.to_string ();
@@ -609,7 +654,7 @@ class GomSerializationTest : GXmlTest  {
 #endif
       assert ("<BookStore/>" in s);
       assert (bs.books == null);
-      bs.create_instance_property ("books");
+      bs.set_instance_property ("books");
       s = bs.to_string ();
       assert (s != null);
 #if DEBUG
@@ -654,6 +699,33 @@ class GomSerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
+    Test.add_func ("/gxml/gom-serialization/mappeable", () => {
+    try {
+      var bs = new BookStand ();
+      assert (bs.hashmap_registers != null);
+      var br = bs.hashmap_registers.create_item () as BookRegister;
+      var book = br.create_book ("Book1");
+      br.book = book;
+      br.year = 2017;
+      bs.hashmap_registers.append (br);
+      assert (bs.hashmap_registers.length == 1);
+      assert (bs.hashmap_registers.has_key ("2017-Book1"));
+      var b1 = bs.hashmap_registers.get ("2017-Book1") as BookRegister;
+      assert (b1 != null);
+      assert (b1.year == 2017);
+      assert (b1.book != null);
+      assert (b1.book.name == "Book1");
+      assert (b1.child_nodes.length == 1);
+      var book2 = b1.create_book ("Book2");
+      b1.append_child (book2);
+      assert (b1.child_nodes.length == 2);
+      b1.book = book2;
+      assert (b1.child_nodes.length == 1);
+    } catch (GLib.Error e) {
+      GLib.message ("Error: "+e.message);
+      assert_not_reached ();
+    }
+    });
     Test.add_func ("/gxml/gom-serialization/write/gom-property", () => {
     try {
       var m = new Motor ();
@@ -779,6 +851,7 @@ class GomSerializationTest : GXmlTest  {
     }
     });
     Test.add_func ("/gxml/gom-serialization/read/bad-node-name", () => {
+      try {
       var b = new Book ();
       b.read_from_string ("<chair name=\"Tall\"/>");
 #if DEBUG
@@ -789,6 +862,10 @@ class GomSerializationTest : GXmlTest  {
       assert (b.child_nodes.size == 0);
       assert (b.owner_document.document_element != null);
       assert (b.owner_document.document_element.node_name == "Book");
+      } catch (GLib.Error e) {
+        GLib.message ("Error: "+e.message);
+        assert_not_reached ();
+      }
     });
     Test.add_func ("/gxml/gom-serialization/read/object-property", () => {
     try {
@@ -800,9 +877,9 @@ class GomSerializationTest : GXmlTest  {
       assert ("<BookRegister Year=\"0\"/>" in s);
       b.read_from_string ("<bookRegister><Book/></bookRegister>");
       s = b.to_string ();
-#if DEBUG
+//#if DEBUG
       GLib.message ("doc:"+s);
-#endif
+//#endif
       assert ("<BookRegister Year=\"0\"><Book/></BookRegister>" in s);
     } catch (GLib.Error e) {
       GLib.message ("Error: "+e.message);


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