[gxml] Collections: added new multi-type collection



commit 06649c8b0b45204d58ee220a153c87770b27c4bc
Author: Daniel Espinosa <esodan gmail com>
Date:   Thu Feb 27 10:52:40 2020 -0600

    Collections: added new multi-type collection
    
    A multi-type collection can recognize more than one
    object direved from the same type, like an interface

 gxml/BaseCollection.vala    |  13 +--
 gxml/Collections.vala       |  86 +++++++++++++++++++
 gxml/Parser.vala            |  64 ++++++++++----
 gxml/XParser.vala           |   5 ++
 gxml/XdParser.vala          |   8 ++
 test/SerializationTest.vala | 200 ++++++++++++++++++++++++++++++++++++++------
 6 files changed, 331 insertions(+), 45 deletions(-)
---
diff --git a/gxml/BaseCollection.vala b/gxml/BaseCollection.vala
index 6cd91d5..c52e278 100644
--- a/gxml/BaseCollection.vala
+++ b/gxml/BaseCollection.vala
@@ -85,12 +85,15 @@ public abstract class GXml.BaseCollection : GLib.Object, Traversable<DomElement>
    * {@inheritDoc}
    */
   public void initialize (GLib.Type items_type) throws GLib.Error {
-    if (!items_type.is_a (typeof (GXml.Element))) {
+    if (!items_type.is_a (typeof (GXml.DomElement))
+        && !items_type.is_a (typeof (GXml.Object))) {
       throw new DomError.INVALID_NODE_TYPE_ERROR
                 (_("Invalid attempt to initialize a collection using an unsupported type. Only 
GXmlGXml.Element is supported"));
     }
-    var o = GLib.Object.new (items_type) as GXml.Element;
-    _items_name = o.local_name;
+    if (!items_type.is_abstract () && items_type.is_instantiatable ()) {
+      var o = GLib.Object.new (items_type) as GXml.Element;
+      _items_name = o.local_name;
+    }
     _items_type = items_type;
   }
   /**
@@ -116,7 +119,7 @@ public abstract class GXml.BaseCollection : GLib.Object, Traversable<DomElement>
   public void append (DomElement node) throws GLib.Error {
     if (_element == null)
       throw new DomError.INVALID_NODE_TYPE_ERROR
-                (_("Parent Element is invalid"));
+                (_("Parent Element is invalid. Set 'element' property at construction time"));
     if (!(node is GXml.Element))
       throw new DomError.INVALID_NODE_TYPE_ERROR
                 (_("Invalid attempt to set unsupported type. Only GXmlGXml.Element is supported"));
@@ -144,7 +147,7 @@ public abstract class GXml.BaseCollection : GLib.Object, Traversable<DomElement>
     clear ();
     if (_element == null)
       throw new DomError.INVALID_NODE_TYPE_ERROR
-                (_("Parent Element is invalid"));
+                (_("Parent Element is invalid. Set 'element' property at construction time"));
     for (int i = 0; i < _element.child_nodes.size; i++) {
       var n = _element.child_nodes.get (i);
       if (n is GXml.Object) {
diff --git a/gxml/Collections.vala b/gxml/Collections.vala
index 4b441d6..0549168 100644
--- a/gxml/Collections.vala
+++ b/gxml/Collections.vala
@@ -337,3 +337,89 @@ public interface GXml.ThreeMap : GLib.Object, GXml.Collection, Traversable<DomEl
    */
   public abstract Set<string> third_keys_set (string pkey, string skey);
 }
+
+/**
+ * Collection to manage child {@link GXml.DomElement} objects
+ * mapped to different classes, derived or child type of
+ * {@link Collection.items_type}
+ *
+ * A collection using {@link Collection.items_type} as a common
+ * parent {@link GLib.Type} of a set of instantiatable {@link GLib.Type}.
+ *
+ * In the next example, is possible to setup a class for Top element,
+ * having a {@link GXml.CollectionParent} implementation class, supporting
+ * reading any kind of derived classes from the {@link Collection.items_type};
+ * for the example, Time, Goal and Reque are implementations of, say, Child
+ * interface, so they will be added to the collection and deserialized
+ * as an instance of the object, based in the node's name.
+ *
+ * {{{
+ * <Top>
+ *   <Time/>
+ *   <Goal/>
+ *   <Resque/>
+ * }}}
+ *
+ * Implementators, should override {@link types} property
+ * setting up a hash table and use {@link add_supported_type} or
+ * {@link add_supported_types} to add one or a set of types to be supported.
+ * {@link types} is used by {@link GXml.Parser} to detect the types
+ * suuported in a collection to create the corresponding objects of the
+ * currect instantiable {@link GLib.Type} at runtime, adding them to the
+ * collection, corresponding to the element's tag's name.
+ */
+public interface GXml.CollectionParent : GLib.Object, GXml.Collection {
+  /**
+   * Creates a hash map with a set of child instantiable {@link GLib.Type}
+   * of the {@link Collection.items_type}
+   *
+   * Implementators, should override this property in order to create
+   * its own collection of supported instantiatable types.
+   */
+  public virtual GLib.HashTable<string,GLib.Type> types {
+    owned get {
+      return new GLib.HashTable<string,GLib.Type> (str_hash, str_equal);
+    }
+  }
+  /**
+   * Insert a new supported instantiatable type in given hash table, by
+   * instantiating the type, getting its node's local name
+   * as key.
+   *
+   * @param types a {@link GLib.HashTable} to hold supported types
+   * @param parent_type a {@link GLib.Type} as parent of supported types, it is
+   * not necesarry to be an instantiatable type, like interfaces, should be
+   * the same of {@link GXml.Collection.items_type}
+   * @param type a supported instantiatable {@link GLib.Type}
+   * to be added in the collection
+   */
+  public static void add_supported_type (GLib.HashTable<string,GLib.Type> types,
+                                        GLib.Type parent_type,
+                                        GLib.Type type)
+    requires (type.is_a (typeof (GXml.Element)))
+  {
+    var o = GLib.Object.new (type) as GXml.Element;
+    string name = o.local_name.down ().dup ();
+    types.insert (name, type);
+  }
+  /**
+   * Insert a set of supported instantiatable type in given hash table, by
+   * instantiating the type, getting its node's local name
+   * as key.
+   *
+   * @param table a {@link GLib.HashTable} to hold supported types
+   * @param parent_type a {@link GLib.Type} as parent of supported types, it is
+   * not necesarry to be an instantiatable type, like interfaces, should be
+   * the same of {@link GXml.Collection.items_type}
+   * @param types an array of supported instantiatable {@link GLib.Type}
+   * to be added in the collection
+   */
+  public static void add_supported_types (GLib.HashTable<string,GLib.Type> table,
+                                        GLib.Type parent_type,
+                                        GLib.Type[] types)
+  {
+    for (int i = 0; i < types.length; i++) {
+      add_supported_type (table, parent_type, types[i]);
+    }
+  }
+}
diff --git a/gxml/Parser.vala b/gxml/Parser.vala
index 9fc661c..dbb83c3 100644
--- a/gxml/Parser.vala
+++ b/gxml/Parser.vala
@@ -51,6 +51,18 @@ public interface GXml.Parser : GLib.Object {
    * A {@link GXml.DomDocument} to read to or write from
    */
   public abstract DomNode node { get; }
+  /**
+   * A collection of child types of found {@link GXml.CollectionParent}
+   * objects, used to instantiate the correct class based on the
+   * node's name.
+   *
+   * The map use the {@link GLib.Type} of the object implementing
+   * {@link GXml.CollectionParent} as the key, to get a {@link GLib.HashTable}
+   * holding a set of instantiable child classes, with the key as the
+   * lowercase of the node's local name to get the {@link GLib.Type}
+   * of the instantiable class to create and add to the collection.
+   */
+  public abstract GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> types { get; }
   /**
    * Writes a {@link GXml.DomDocument} to a {@link GLib.File}
    */
@@ -252,23 +264,43 @@ public interface GXml.Parser : GLib.Object {
         throw new DomError.INVALID_NODE_TYPE_ERROR
                     (_("Collection '%s' hasn't been constructed properly: items' type property was not set 
at construction time or set to invalid type"), col.get_type ().name ());
       }
-      if (col.items_name == "" || col.items_name == null) {
-        throw new DomError.INVALID_NODE_TYPE_ERROR
-                    (_("Collection '%s' hasn't been constructed properly: items' name property was not set 
at construction time"), col.get_type ().name ());
-      }
-      if (col.element == null || !(col.element is GXml.Object)) {
-        throw new DomError.INVALID_NODE_TYPE_ERROR
-                    (_("Collection '%s' hasn't been constructed properly: element property was not set at 
construction time"), col.get_type ().name ());
-      }
-      if (!(col.element is GXml.Object)) {
-        throw new DomError.INVALID_NODE_TYPE_ERROR
-                    (_("Invalid object of type '%s' doesn't implement GXml.Object interface: can't be 
handled by the collection"), col.element.get_type ().name ());
+      GLib.Type obj_type = GLib.Type.INVALID;
+      if (col is CollectionParent) {
+        HashTable<string,GLib.Type> obj_types = null;
+        if (!types.contains (col.get_type ())) {
+          obj_types = ((CollectionParent) col).types;
+          types.insert (col.get_type (), obj_types);
+        } else {
+          obj_types = types.lookup (col.get_type ());
+        }
+        if (obj_types != null) {
+          string n = current_node_name ().down ();
+          if (obj_types.contains (n)) {
+            obj_type = obj_types.lookup (n);
+          }
+        }
+      } else {
+        if (col.items_name == "" || col.items_name == null) {
+          throw new DomError.INVALID_NODE_TYPE_ERROR
+                      (_("Collection '%s' hasn't been constructed properly: items' name property was not set 
at construction time"), col.get_type ().name ());
+        }
+        if (col.element == null || !(col.element is GXml.Object)) {
+          throw new DomError.INVALID_NODE_TYPE_ERROR
+                      (_("Collection '%s' hasn't been constructed properly: element property was not set at 
construction time"), col.get_type ().name ());
+        }
+        if (!(col.element is GXml.Object)) {
+          throw new DomError.INVALID_NODE_TYPE_ERROR
+                      (_("Invalid object of type '%s' doesn't implement GXml.Object interface: can't be 
handled by the collection"), col.element.get_type ().name ());
+        }
+        if (col.items_name.down () == current_node_name ().down ()) {
+          if (parent.owner_document == null)
+            throw new DomError.HIERARCHY_REQUEST_ERROR
+                        (_("No document is set to node"));
+            obj_type = col.items_type;
+        }
       }
-      if (col.items_name.down () == current_node_name ().down ()) {
-        if (parent.owner_document == null)
-          throw new DomError.HIERARCHY_REQUEST_ERROR
-                      (_("No document is set to node"));
-        var obj = GLib.Object.new (col.items_type,
+      if (obj_type != GLib.Type.INVALID) {
+        var obj = GLib.Object.new (obj_type,
                               "owner-document", node.owner_document) as DomElement;
         parent.append_child (obj);
         read_element (obj as DomElement);
diff --git a/gxml/XParser.vala b/gxml/XParser.vala
index abfc402..f0266b3 100644
--- a/gxml/XParser.vala
+++ b/gxml/XParser.vala
@@ -32,9 +32,13 @@ public class GXml.XParser : GLib.Object, GXml.Parser {
   private TextReader tr;
   private Xml.TextWriter tw;
   private DataInputStream tistream;
+  private GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> _types;
 
   public bool backup { get; set; }
   public bool indent { get; set; }
+  public GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> types {
+    get { return _types; }
+  }
 
   public DomNode node { get { return _node; } }
 
@@ -54,6 +58,7 @@ public class GXml.XParser : GLib.Object, GXml.Parser {
     indent = false;
     cancellable = null;
     tistream = null;
+    _types = new GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> (int_hash, int_equal);
   }
 
   public void write_stream (OutputStream stream) throws GLib.Error {
diff --git a/gxml/XdParser.vala b/gxml/XdParser.vala
index aa87d9d..12353d3 100644
--- a/gxml/XdParser.vala
+++ b/gxml/XdParser.vala
@@ -27,6 +27,11 @@
 private class GXml.XdParser : GLib.Object, Parser {
   private XDocument document;
   private DomNode _node;
+  private GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> _types;
+
+  construct {
+    _types = new GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> (int_hash, int_equal);
+  }
 
   public XdParser (XDocument doc) {
     document = doc;
@@ -110,6 +115,9 @@ private class GXml.XdParser : GLib.Object, Parser {
        }
        public bool backup { get; set; }
        public bool indent { get; set; }
+  public GLib.HashTable<GLib.Type,GLib.HashTable<string,GLib.Type>> types {
+    get { return _types; }
+  }
        public GXml.DomNode node { get { return _node; } }
        public Cancellable? cancellable { get; set; }
 }
diff --git a/test/SerializationTest.vala b/test/SerializationTest.vala
index b7c0452..87e4ba7 100644
--- a/test/SerializationTest.vala
+++ b/test/SerializationTest.vala
@@ -215,6 +215,93 @@ class GomBasicTypes : GXml.Element {
   }
 }
 
+interface Item : GLib.Object, GXml.Object {}
+
+
+class Items : GXml.ArrayList, GXml.CollectionParent {
+  construct {
+    try { initialize (typeof (Item)); } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+  // Override supported types
+  public GLib.HashTable<string,GLib.Type> types {
+    owned get {
+      var c = new GLib.HashTable<string,GLib.Type> (str_hash, str_equal);
+      GXml.CollectionParent.add_supported_types (c, items_type,
+                                                {
+                                                typeof (Monitor),
+                                                typeof (Keyword),
+                                                typeof (Cpu)
+                                                });
+      return c;
+    }
+  }
+}
+
+class Monitor : GXml.Element, Item {
+  [Description (nick="::size")]
+  public int size { get; set; }
+  construct {
+    try { initialize ("Monitor"); } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+}
+class Keyword : GXml.Element, Item {
+  [Description (nick="::language")]
+  public string size { get; set; }
+  construct {
+    try { initialize ("Keyword"); } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+}
+class Cpu : GXml.Element, Item {
+  [Description (nick="::language")]
+  public string size { get; set; }
+  construct {
+    try { initialize ("Cpu"); } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+}
+
+abstract interface Container : GLib.Object, GXml.Object {}
+
+class StoreShelf : GXml.Element, Container {
+  [Description (nick="::Id")]
+  public int id { get; set; default = 1; }
+  public Items items { get; set; }
+  construct {
+    try {
+      initialize ("Shelf");
+      set_instance_property ("items");
+    } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+}
+
+class Containers : GXml.ArrayList, GXml.CollectionParent {
+  construct {
+    var s = new StoreShelf ();
+    try { initialize (typeof (Container)); } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+  // Override supported types
+  public GLib.HashTable<string,GLib.Type> types {
+    owned get {
+      var c = new GLib.HashTable<string,GLib.Type> (str_hash, str_equal);
+      GXml.CollectionParent.add_supported_type (c, items_type, typeof (StoreShelf));
+      assert (c.contains ("shelf"));
+      assert (c.lookup ("shelf") != GLib.Type.INVALID);
+      assert (((GLib.Type) c.lookup ("shelf")) == typeof (StoreShelf));
+      return c;
+    }
+  }
+}
+class ComputerStore : GXml.Element {
+  [Description (nick="::name")]
+  public string name { get; set; }
+  public Containers containers { get; set; default = new Containers (); }
+  construct {
+    try {
+      initialize ("ComputerStore");
+      set_instance_property ("containers");
+    } catch (GLib.Error e) { warning  ("Error: %s", e.message); }
+  }
+}
+
 class SerializationTest : GXmlTest  {
   public class Book : GXml.Element {
     [Description (nick="::Name")]
@@ -545,7 +632,7 @@ class SerializationTest : GXmlTest  {
     }
   }
   public static void add_tests () {
-    Test.add_func ("/gxml/gom-serialization/write/properties", () => {
+    Test.add_func ("/gxml/serialization/write/properties", () => {
     try {
       var b = new Book ();
       var parser = new XParser (b);
@@ -563,7 +650,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-ignore", () => {
+    Test.add_func ("/gxml/serialization/write/property-ignore", () => {
       var c = new Computer ();
       string s = c.to_string ();
       assert (s != null);
@@ -579,7 +666,7 @@ class SerializationTest : GXmlTest  {
       GLib.message ("DOC:"+s);
 #endif
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-long-name", () => {
+    Test.add_func ("/gxml/serialization/write/property-long-name", () => {
       var t = new Taxes ();
       string s = t.to_string ();
       assert (s != null);
@@ -608,7 +695,7 @@ class SerializationTest : GXmlTest  {
       GLib.message ("DOC:"+s);
 #endif
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-date", () => {
+    Test.add_func ("/gxml/serialization/write/property-date", () => {
       var t = new Taxes ();
       string s = t.to_string ();
       assert (s != null);
@@ -644,7 +731,7 @@ class SerializationTest : GXmlTest  {
       assert (gd.get_date ().valid ());
       assert (gd.value == "2076-03-17");
     });
-    Test.add_func ("/gxml/gom-serialization/read/property-date", () => {
+    Test.add_func ("/gxml/serialization/read/property-date", () => {
     try {
       var t = new Taxes ();
       t.read_from_string ("<Taxes PayDate=\"2050-12-09\"/>");
@@ -666,7 +753,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-datetime", () => {
+    Test.add_func ("/gxml/serialization/write/property-datetime", () => {
       var t = new Taxes ();
       string s = t.to_string ();
       assert (s != null);
@@ -699,7 +786,7 @@ class SerializationTest : GXmlTest  {
 #endif
       assert ("Timestamp=\"2023-03-10T15:23:10\"" in s2);
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-arraylist", () => {
+    Test.add_func ("/gxml/serialization/write/property-arraylist", () => {
     try {
       var bs = new BookStand ();
       string s = bs.to_string ();
@@ -779,7 +866,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/property-hashmap", () => {
+    Test.add_func ("/gxml/serialization/write/property-hashmap", () => {
     try {
       var bs = new BookStore ();
       string s = bs.to_string ();
@@ -834,7 +921,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/mappeable", () => {
+    Test.add_func ("/gxml/serialization/mappeable", () => {
     try {
       var bs = new BookStand ();
       assert (bs.hashmap_registers != null);
@@ -861,7 +948,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/mappeablepairedkey", () => {
+    Test.add_func ("/gxml/serialization/write/mappeablepairedkey", () => {
     try {
       var bs = new BookStand ();
       assert (bs.hashpair_registers != null);
@@ -883,7 +970,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/mappeablethreekey", () => {
+    Test.add_func ("/gxml/serialization/write/mappeablethreekey", () => {
     try {
       var bs = new BookStand ();
       assert (bs.hashthree_registers != null);
@@ -925,7 +1012,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/write/gom-property", () => {
+    Test.add_func ("/gxml/serialization/write/property", () => {
       var m = new Motor ();
       string s = m.to_string ();
       assert (s != null);
@@ -1008,7 +1095,7 @@ class SerializationTest : GXmlTest  {
       m.model.value = "MODEL1";
       assert (m.model.is_valid_value ());
     });
-    Test.add_func ("/gxml/gom-serialization/read/properties", () => {
+    Test.add_func ("/gxml/serialization/read/properties", () => {
     try {
       var b = new Book ();
       var parser = new XParser (b);
@@ -1036,7 +1123,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/read/bad-node-name", () => {
+    Test.add_func ("/gxml/serialization/read/bad-node-name", () => {
       try {
       var b = new Book ();
       b.read_from_string ("<chair name=\"Tall\"/>");
@@ -1053,7 +1140,7 @@ class SerializationTest : GXmlTest  {
         assert_not_reached ();
       }
     });
-    Test.add_func ("/gxml/gom-serialization/read/object-property", () => {
+    Test.add_func ("/gxml/serialization/read/object-property", () => {
     try {
       var b = new BookRegister ();
       string s = b.to_string ();
@@ -1072,7 +1159,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/read/gom-property", () => {
+    Test.add_func ("/gxml/serialization/read/property", () => {
     try {
       var m = new Motor ();
       string s = m.to_string ();
@@ -1108,7 +1195,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/read/property-arraylist", () => {
+    Test.add_func ("/gxml/serialization/read/property-arraylist", () => {
     try {
       var bs = new BookStand ();
       string s = bs.to_string ();
@@ -1145,7 +1232,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/read/property-hashmap", () => {
+    Test.add_func ("/gxml/serialization/read/property-hashmap", () => {
     try {
       var bs = new BookStand ();
       string s = bs.to_string ();
@@ -1190,7 +1277,7 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/multiple-child-collections",
+    Test.add_func ("/gxml/serialization/multiple-child-collections",
     () => {
       try {
         double time;
@@ -1250,7 +1337,7 @@ class SerializationTest : GXmlTest  {
         assert_not_reached ();
       }
     });
-    Test.add_func ("/gxml/gom-serialization/collections/hashpairedmap/keys",
+    Test.add_func ("/gxml/serialization/collections/hashpairedmap/keys",
     () => {
       try {
         var ops = new Operations ();
@@ -1302,7 +1389,7 @@ class SerializationTest : GXmlTest  {
         assert_not_reached ();
       }
     });
-    Test.add_func ("/gxml/gom-serialization/collections/hashthreemap/keys",
+    Test.add_func ("/gxml/serialization/collections/hashthreemap/keys",
     () => {
       try {
         var ks = new ThreeKeys ();
@@ -1358,7 +1445,7 @@ class SerializationTest : GXmlTest  {
         assert_not_reached ();
       }
     });
-    Test.add_func ("/gxml/gom-serialization/basic-types",
+    Test.add_func ("/gxml/serialization/basic-types",
     () => {
       try {
         var bt = new GomBasicTypes ();
@@ -1399,7 +1486,7 @@ class SerializationTest : GXmlTest  {
         assert_not_reached ();
       }
     });
-    Test.add_func ("/gxml/gom-serialization/collection/iteration", () => {
+    Test.add_func ("/gxml/serialization/collection/iteration", () => {
     try {
       var bs = new BookStore ();
       assert (bs.books == null);
@@ -1502,12 +1589,77 @@ class SerializationTest : GXmlTest  {
       assert_not_reached ();
     }
     });
-    Test.add_func ("/gxml/gom-serialization/attribute-gobject", () => {
+    Test.add_func ("/gxml/serialization/attribute-gobject", () => {
       var tk = new ThreeKey ();
       assert (tk.code == null);
       assert (tk.get_attribute ("Code") == null);
       tk.code = "code";
       assert (tk.get_attribute ("Code") == "code");
     });
+    Test.add_func ("/gxml/serialization/collection-parent/list/read", () => {
+      try {
+        var cs = new ComputerStore ();
+        assert (cs.containers.types.contains ("shelf"));
+        assert (cs.containers.types.lookup ("shelf") == typeof (StoreShelf));
+        string str = """
+  <ComputerStore><Shelf Id="2"><Cpu/></Shelf></ComputerStore>
+  """;
+        cs.read_from_string (str);
+        bool found_shelf = false;
+        bool found_cpu = false;
+        foreach (GXml.DomNode n in cs.child_nodes) {
+          message ("Found Type: %s", n.get_type ().name ());
+          if (n is StoreShelf) {
+            found_shelf = true;
+            foreach (GXml.DomNode cn in n.child_nodes) {
+              message ("Found Type: %s", n.get_type ().name ());
+              if (cn is Cpu) {
+                found_cpu = true;
+              }
+            }
+          }
+        }
+        assert (found_shelf);
+        assert (found_cpu);
+        message ("Read:\n%s", cs.write_string ());
+        assert ("""<ComputerStore><Shelf Id="2"><Cpu/></Shelf></ComputerStore>""" in cs.write_string ());
+        str = """
+  <ComputerStore><Shelf Id="2"><Cpu/><Monitor size="32"/><Keyword/></Shelf></ComputerStore>
+  """;
+        cs = new ComputerStore ();
+        cs.read_from_string (str);
+        message ("Read:\n%s", cs.write_string ());
+        assert ("""<ComputerStore><Shelf Id="2"><Cpu/><Monitor 
size="32"/><Keyword/></Shelf></ComputerStore>""" in cs.write_string ());
+        found_shelf = false;
+        bool found_monitor = false;
+        bool found_keyword = false;
+        found_cpu = false;
+        foreach (GXml.DomNode n in cs.child_nodes) {
+          message ("Found Type: %s", n.get_type ().name ());
+          if (n is StoreShelf) {
+            found_shelf = true;
+            foreach (GXml.DomNode cn in n.child_nodes) {
+              message ("Found Type: %s", n.get_type ().name ());
+              if (cn is Monitor) {
+                found_monitor = true;
+                assert (((Monitor) cn).size == 32);
+              }
+              if (cn is Keyword) {
+                found_keyword = true;
+              }
+              if (cn is Cpu) {
+                found_cpu = true;
+              }
+            }
+          }
+        }
+        assert (found_shelf);
+        assert (found_monitor);
+        assert (found_keyword);
+        assert (found_cpu);
+      } catch (GLib.Error e) {
+        warning ("Error: %s", e.message);
+      }
+    });
   }
 }


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