diff --git a/gxml/GomObject.vala b/gxml/GomObject.vala index f2586fd327b65df2afab073f60d1cb4274ad9e19..a31bdd966106ef2e9b56be1cf3bf4c2670b66b77 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); @@ -296,6 +296,25 @@ 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. @@ -303,13 +322,6 @@ public interface GXml.GomObject : GLib.Object, 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 329c3c3dd9a457484bdbc48b028dcdeccd2062a7..6cdea5196ef0da3d4b3d9155aa881b1317d0bec0 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 3671b8fa367c04a48a1f578891872f95fe25d5d6..5e12e60a7d542df7606d52003d0b5c26673a6785 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 ("" 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 ("" 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 ("" 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 ("" 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 (""); #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 ("" in s); b.read_from_string (""); s = b.to_string (); -#if DEBUG +//#if DEBUG GLib.message ("doc:"+s); -#endif +//#endif assert ("" in s); } catch (GLib.Error e) { GLib.message ("Error: "+e.message);