[gxml/serialization: 1/10] Initial setup of XmlObjectModel to Serializable interface
- From: Daniel Espinosa Ortiz <despinosa src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gxml/serialization: 1/10] Initial setup of XmlObjectModel to Serializable interface
- Date: Wed, 24 Jul 2013 03:34:22 +0000 (UTC)
commit 349673ea440f3cfdaeca5c27279171ced27b3e99
Author: Daniel Espinosa <esodan gmail com>
Date: Mon Jul 22 12:32:35 2013 -0500
Initial setup of XmlObjectModel to Serializable interface
gxml/Serializable.vala | 180 +++++++++++++++++++++++++++++++++++++++-----
gxml/Serialization.vala | 27 ++++---
test/SerializableTest.vala | 141 ++++++++++++++++++++++++++++++-----
3 files changed, 300 insertions(+), 48 deletions(-)
---
diff --git a/gxml/Serializable.vala b/gxml/Serializable.vala
index 7687032..cca867d 100644
--- a/gxml/Serializable.vala
+++ b/gxml/Serializable.vala
@@ -2,6 +2,7 @@
/* Serializable.vala
*
* Copyright (C) 2011-2013 Richard Schwarting <aquarichy gmail com>
+ + Copyright (C) 2013 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
@@ -18,6 +19,7 @@
*
* Authors:
* Richard Schwarting <aquarichy gmail com>
+ * Daniel Espinosa <esodan gmail com>
*/
@@ -69,6 +71,50 @@ namespace GXml {
* For an example, look in tests/XmlSerializableTest
*/
public interface Serializable : GLib.Object {
+ public abstract bool serializable_property_use_blurb { get; set; }
+ /**
+ * Store all properties to be ignored on serialization.
+ *
+ * Implementors: By default { link list_serializable_properties} initialize
+ * this property to store all public properties, except this one.
+ */
+ public abstract HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get;
protected set; }
+ /**
+ * On deserialization stores any { link DomNode} not used on this
+ * object, but exists in current XML file.
+ *
+ * This property must be ignored on serialisation.
+ */
+ public abstract HashTable<string,GXml.DomNode> unknown_serializable_property { get;
protected set; }
+
+ /**
+ * Used by to add properties and values to DomNode.
+ *
+ * This property must be ignored on serialisation.
+ */
+ public abstract Element serialized_xml_node { get; protected set; }
+
+ /**
+ * Used by to add properties and values to DomNode.
+ *
+ * This property must be ignored on serialisation.
+ */
+ public abstract string serialized_xml_node_value { get; protected
set; }
+
+ /**
+ * Serialize this object.
+ *
+ * @doc an GXml.Document object to serialise to
+ */
+ public virtual DomNode? serialize (Document doc) throws DomError
+ {
+ serialized_xml_node = doc.create_element (this.get_type ().name ());
+ foreach (ParamSpec spec in list_serializable_properties ()) {
+ serialize_property (spec, doc);
+ }
+ serialized_xml_node.node_value = serialized_xml_node_value;
+ return serialized_xml_node;
+ }
/**
* Handles deserializing individual properties.
*
@@ -100,8 +146,34 @@ namespace GXml {
* letting them get name from spec
* @todo: consider returning { link GLib.Value} as out param
*/
- public virtual bool deserialize_property (string property_name, /* out GLib.Value value,*/
GLib.ParamSpec spec, GXml.DomNode property_node) {
- return false; // default deserialize_property gets used
+ public virtual bool deserialize_property (GLib.ParamSpec spec,
+ GXml.DomNode property_node)
+ {
+ bool ret = false;
+ var prop = find_property_spec (property_node.node_name);
+ if (prop == null) {
+ unknown_serializable_property.set (property_node.node_name, property_node);
+ return false;
+ }
+ Value val = Value (prop.value_type);
+ if (Value.type_transformable (typeof (DomNode), prop.value_type)) {
+ Value tmp = Value (typeof (DomNode));
+ tmp.set_object (property_node);
+ ret = tmp.transform (ref val);
+ }
+ else {
+ if (property_node is GXml.Attr) {
+ if (Value.type_transformable (typeof (string), prop.value_type)) {
+ Value ptmp = Value (typeof (string));
+ ptmp.set_string (property_node.node_value);
+ ret = ptmp.transform (ref val);
+ }
+ }
+ }
+ if (ret) {
+ set_property (prop.name, val);
+ }
+ return ret;
}
/**
@@ -126,15 +198,36 @@ namespace GXml {
* @param doc the { link GXml.Document} the returned { link GXml.DomNode} should belong to
* @return a new { link GXml.DomNode}, or `null`
*/
- /*
- * @todo: consider not giving property_name, let them get name from spec?
- */
- public virtual GXml.DomNode? serialize_property (string property_name, /*GLib.Value value, */
GLib.ParamSpec spec, GXml.Document doc) {
- return null; // default serialize_property gets used
+ public virtual GXml.DomNode? serialize_property (GLib.ParamSpec spec,
+ GXml.Document doc)
+ throws DomError
+ {
+ var prop = find_property_spec (spec.name);
+ if (prop == null)
+ return null;
+ if (prop.value_type == typeof (Serializable)) {
+ var v = Value (typeof (Object));
+ get_property (spec.name, ref v);
+ var obj = (Serializable) v.get_object ();
+ var node = obj.serialize (doc);
+ serialized_xml_node.append_child (node);
+ }
+ Value oval = Value (spec.value_type);
+ get_property (spec.name, ref oval);
+ string val = "";
+ if (Value.type_transformable (spec.value_type, typeof (string)))
+ {
+ Value rval = Value (typeof (string));
+ oval.transform (ref rval);
+ val = rval.dup_string ();
+ }
+ string attr_name = spec.name;
+ if (serializable_property_use_blurb)
+ attr_name = spec.get_blurb ();
+ serialized_xml_node.set_attribute (attr_name, val);
+ return (DomNode) serialized_xml_node.get_attribute_node (attr_name);
}
- /* Correspond to: g_object_class_{find_property,list_properties} */
-
/*
* Handles finding the { link GLib.ParamSpec} for a given property.
*
@@ -163,8 +256,36 @@ namespace GXml {
* { link GLib.ParamSpec} s separately, rather than creating new
* ones for each call.
*/
- public virtual unowned GLib.ParamSpec? find_property (string property_name) {
- return this.get_class ().find_property (property_name); // default
+ public virtual GLib.ParamSpec? find_property_spec (string property_name) {
+ init_properties ();
+ if (!ignored_serializable_properties.contains (property_name)) {
+ return get_class ().find_property (property_name);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to initialize { link ignored_serializable_properties} property
+ * and default not to be serialized properties. Unless you override any function
+ * is not required to be called at class implementor's construction time.
+ *
+ */
+ public virtual void init_properties ()
+ {
+ if (ignored_serializable_properties == null) {
+ ignored_serializable_properties = new HashTable<string,ParamSpec> (str_hash,
str_equal);
+ ignored_serializable_properties.set ("ignored-serializable-properties",
+
get_class
().find_property("ignored-serializable-properties"));
+ ignored_serializable_properties.set ("unknown-serializable-property",
+
get_class
().find_property("unknown-serializable-property"));
+ ignored_serializable_properties.set ("serialized-xml-node",
+
get_class
().find_property("serialized-xml-node"));
+ ignored_serializable_properties.set ("serialized-xml-node-value",
+
get_class
().find_property("serialized-xml-node-value"));
+ }
+ if (unknown_serializable_property == null) {
+ unknown_serializable_property = new HashTable<string,GXml.DomNode> (str_hash,
str_equal);
+ }
}
/*
@@ -195,8 +316,16 @@ namespace GXml {
* { link GLib.ParamSpec} s separately, rather than creating new
* ones for each call.
*/
- public virtual unowned GLib.ParamSpec[] list_properties () {
- return this.get_class ().list_properties ();
+ public virtual GLib.ParamSpec[] list_serializable_properties ()
+ {
+ init_properties ();
+ ParamSpec[] props = {};
+ foreach (ParamSpec spec in this.get_class ().list_properties ()) {
+ if (!ignored_serializable_properties.contains (spec.name)) {
+ props += spec;
+ }
+ }
+ return props;
}
/*
@@ -228,14 +357,26 @@ namespace GXml {
* @todo: why not just return a string? :D Who cares
* how analogous it is to { link GLib.Object.get_property}? :D
*/
- public virtual void get_property (GLib.ParamSpec spec, ref GLib.Value str_value) {
- ((GLib.Object)this).get_property (spec.name, ref str_value);
+ public virtual string get_property_value (GLib.ParamSpec spec)
+ {
+ Value val = Value (spec.value_type);
+ if (!ignored_serializable_properties.contains (spec.name))
+ {
+ Value ret = "";
+ ((GLib.Object)this).get_property (spec.name, ref val);
+ if (Value.type_transformable (val.type (), typeof (string)))
+ {
+ val.transform (ref ret);
+ return ret.dup_string ();
+ }
+ }
+ return "";
}
/*
* Set a property's value.
*
* @param spec Specifies the property whose value will be set
- * @param value The value to set the property to.
+ * @param val The value to set the property to.
*
* { link GXml.Serialization} uses { link GLib.Object.set_property} (as
* well as { link GLib.ObjectClass.find_property},
@@ -253,8 +394,11 @@ namespace GXml {
* handle this case as a virtual property, supported
* by the other { link GXml.Serializable} functions.
*/
- public virtual void set_property (GLib.ParamSpec spec, GLib.Value value) {
- ((GLib.Object)this).set_property (spec.name, value);
+ public virtual void set_property_value (GLib.ParamSpec spec, GLib.Value val)
+ {
+ if (!ignored_serializable_properties.contains (spec.name)) {
+ ((GLib.Object)this).set_property (spec.name, val);
+ }
}
}
}
diff --git a/gxml/Serialization.vala b/gxml/Serialization.vala
index 639469b..2485330 100644
--- a/gxml/Serialization.vala
+++ b/gxml/Serialization.vala
@@ -71,6 +71,7 @@ namespace GXml {
* serialization themselves, including non-public properties or
* data types not automatically supported by { link GXml.Serialization}.
*/
+
public class Serialization : GLib.Object {
private static void print_debug (GXml.Document doc, GLib.Object object) {
stdout.printf ("Object XML\n---\n%s\n", doc.to_string ());
@@ -113,7 +114,7 @@ namespace GXml {
former by list_properties) */
value = Value (typeof (int));
if (serializable != null) {
- serializable.get_property (prop_spec, ref value);
+ serializable.get_property (prop_spec.name, ref value);
} else {
object.get_property (prop_spec.name, ref value);
}
@@ -123,7 +124,7 @@ namespace GXml {
} else if (Value.type_transformable (prop_spec.value_type, typeof (string))) { //
e.g. int, double, string, bool
value = Value (typeof (string));
if (serializable != null) {
- serializable.get_property (prop_spec, ref value);
+ serializable.get_property (prop_spec.name, ref value);
} else {
object.get_property (prop_spec.name, ref value);
}
@@ -158,7 +159,7 @@ namespace GXml {
// TODO: this is going to get complicated
value = Value (typeof (GLib.Object));
if (serializable != null) {
- serializable.get_property (prop_spec, ref value);
+ serializable.get_property (prop_spec.name, ref value);
} else {
object.get_property (prop_spec.name, ref value);
/* This can fail; consider case of Gee.TreeSet that isn't special
cased above, gets error
@@ -250,7 +251,7 @@ namespace GXml {
size in our interface's list_properties (), using
[CCode (array_length_type = "guint")] */
if (serializable != null) {
- prop_specs = serializable.list_properties ();
+ prop_specs = serializable.list_serializable_properties ();
} else {
prop_specs = object.get_class ().list_properties ();
}
@@ -267,7 +268,7 @@ namespace GXml {
value_prop = null;
if (serializable != null) {
- value_prop = serializable.serialize_property (prop_spec.name,
prop_spec, doc);
+ value_prop = serializable.serialize_property (prop_spec, doc);
}
if (value_prop == null) {
value_prop = Serialization.serialize_property (object,
prop_spec, doc);
@@ -394,7 +395,6 @@ namespace GXml {
Object obj;
unowned ObjectClass obj_class;
ParamSpec[] specs;
- bool property_found;
Serializable serializable = null;
obj_elem = (Element)node;
@@ -426,7 +426,7 @@ namespace GXml {
}
if (serializable != null) {
- specs = serializable.list_properties ();
+ specs = serializable.list_serializable_properties ();
} else {
specs = obj_class.list_properties ();
}
@@ -447,7 +447,7 @@ namespace GXml {
// Check name and type for property
ParamSpec? spec = null;
if (serializable != null) {
- spec = serializable.find_property (pname);
+ spec = serializable.find_property_spec (pname);
} else {
spec = obj_class.find_property (pname);
}
@@ -461,12 +461,12 @@ namespace GXml {
bool serialized = false;
if (serializable != null) {
- serialized = serializable.deserialize_property
(spec.name, /* out val, */ spec, prop_elem); // TODO: consider rearranging these or the ones in Serializer to
match
+ serialized = serializable.deserialize_property (spec,
prop_elem); // TODO: consider rearranging these or the ones in Serializer to match
}
if (!serialized) {
Serialization.deserialize_property (spec, prop_elem,
out val);
if (serializable != null) {
- serializable.set_property (spec, val);
+ serializable.set_property (spec.name, val);
} else {
obj.set_property (pname, val);
}
@@ -509,7 +509,9 @@ namespace GXml {
* @todo: what do functions written in Vala return in C when
* they throw an exception? NULL/0/FALSE?
*/
- public static bool string_to_gvalue (string str, ref GLib.Value dest) throws
SerializationError {
+ public static bool string_to_gvalue (string str, ref GLib.Value dest)
+ throws SerializationError
+ {
Type t = dest.type ();
GLib.Value dest2 = Value (t);
bool ret = false;
@@ -590,4 +592,5 @@ namespace GXml {
}
}
}
-}
\ No newline at end of file
+}
+
diff --git a/test/SerializableTest.vala b/test/SerializableTest.vala
index 7ae4bb0..34e9af8 100644
--- a/test/SerializableTest.vala
+++ b/test/SerializableTest.vala
@@ -25,24 +25,35 @@ using Gee;
Test overriding {set,get}_property
*/
-public class SerializableTomato : GLib.Object, GXml.Serializable {
+public class SerializableTomato : GLib.Object, GXml.Serializable
+{
+ /* Serializable abstract properties */
+ public GLib.HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get; private set; }
+ public bool serializable_property_use_blurb { get; set; }
+ public GXml.Element serialized_xml_node { get; protected set; }
+ public string serialized_xml_node_value { get; protected set; }
+ public GLib.HashTable<string,GXml.DomNode> unknown_serializable_property { get; private set; }
+
public int weight;
private int age { get; set; }
public int height { get; set; }
public string description { get; set; }
- public SerializableTomato (int weight, int age, int height, string description) {
+ public SerializableTomato (int weight, int age, int height, string description)
+ {
this.weight = weight;
this.age = age;
this.height = height;
this.description = description;
}
- public string to_string () {
+ public string to_string ()
+ {
return "SerializableTomato {weight:%d, age:%d, height:%d, description:%s}".printf (weight,
age, height, description);
}
- public static bool equals (SerializableTomato a, SerializableTomato b) {
+ public static bool equals (SerializableTomato a, SerializableTomato b)
+ {
bool same = (a.weight == b.weight &&
a.age == b.age &&
a.height == b.height &&
@@ -52,13 +63,22 @@ public class SerializableTomato : GLib.Object, GXml.Serializable {
}
}
-public class SerializableCapsicum : GLib.Object, GXml.Serializable {
+public class SerializableCapsicum : GLib.Object, GXml.Serializable
+{
+ /* Serializable abstract properties */
+ public GLib.HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get; private set; }
+ public bool serializable_property_use_blurb { get; set; }
+ public GXml.Element serialized_xml_node { get; protected set; }
+ public string serialized_xml_node_value { get; protected set; }
+ public GLib.HashTable<string,GXml.DomNode> unknown_serializable_property { get; private set; }
+
public int weight;
private int age { get; set; }
public int height { get; set; }
public unowned GLib.List<int> ratings { get; set; }
- public string to_string () {
+ public string to_string ()
+ {
string str = "SerializableCapsicum {weight:%d, age:%d, height:%d, ratings:".printf (weight,
age, height);
foreach (int rating in ratings) {
str += "%d ".printf (rating);
@@ -67,7 +87,8 @@ public class SerializableCapsicum : GLib.Object, GXml.Serializable {
return str;
}
- public SerializableCapsicum (int weight, int age, int height, GLib.List<int> ratings) {
+ public SerializableCapsicum (int weight, int age, int height, GLib.List<int> ratings)
+ {
this.weight = weight;
this.age = age;
this.height = height;
@@ -78,11 +99,12 @@ public class SerializableCapsicum : GLib.Object, GXml.Serializable {
Want an example using GBoxed too
Perhaps these shouldn't be object methods, perhaps they should be static?
Can't have static methods in an interface :(, right? */
- public bool deserialize_property (string property_name, /* out GLib.Value value, */
- GLib.ParamSpec spec, GXml.DomNode property_node) {
+ public bool deserialize_property (GLib.ParamSpec spec,
+ GXml.DomNode property_node)
+ {
GLib.Value outvalue = GLib.Value (typeof (int));
- switch (property_name) {
+ switch (spec.name) {
case "ratings":
this.ratings = new GLib.List<int> ();
foreach (GXml.DomNode rating in property_node.child_nodes) {
@@ -95,17 +117,20 @@ public class SerializableCapsicum : GLib.Object, GXml.Serializable {
this.height = (int)outvalue.get_int64 () - 1;
return true;
default:
- Test.message ("Wasn't expecting the SerializableCapsicum property '%s'",
property_name);
+ Test.message ("Wasn't expecting the SerializableCapsicum property '%s'", spec.name);
assert_not_reached ();
}
return false;
}
- public GXml.DomNode? serialize_property (string property_name, /*GLib.Value value,*/ GLib.ParamSpec
spec, GXml.Document doc) {
+ public GXml.DomNode? serialize_property (GLib.ParamSpec spec,
+ GXml.Document doc)
+ throws GXml.DomError
+ {
GXml.Element c_prop;
GXml.Element rating;
- switch (property_name) {
+ switch (spec.name) {
case "ratings":
GXml.DocumentFragment frag = doc.create_document_fragment ();
try {
@@ -122,7 +147,7 @@ public class SerializableCapsicum : GLib.Object, GXml.Serializable {
case "height":
return doc.create_text_node ("%d".printf (height + 1));
default:
- Test.message ("Wasn't expecting the SerializableCapsicum property '%s'",
property_name);
+ Test.message ("Wasn't expecting the SerializableCapsicum property '%s'", spec.name);
assert_not_reached ();
}
@@ -132,7 +157,15 @@ public class SerializableCapsicum : GLib.Object, GXml.Serializable {
}
-public class SerializableBanana : GLib.Object, GXml.Serializable {
+public class SerializableBanana : GLib.Object, GXml.Serializable
+{
+ /* Serializable abstract properties */
+ public GLib.HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get; private set; }
+ public bool serializable_property_use_blurb { get; set; }
+ public GXml.Element serialized_xml_node { get; protected set; }
+ public string serialized_xml_node_value { get; protected set; }
+ public GLib.HashTable<string,GXml.DomNode> unknown_serializable_property { get; private set; }
+
private int private_field;
public int public_field;
private int private_property { get; set; }
@@ -184,7 +217,7 @@ public class SerializableBanana : GLib.Object, GXml.Serializable {
return null;
}
- public void get_property (GLib.ParamSpec spec, ref GLib.Value str_value) {
+ public new void get_property (GLib.ParamSpec spec, ref GLib.Value str_value) {
Value value = Value (typeof (int));
switch (spec.name) {
@@ -209,7 +242,8 @@ public class SerializableBanana : GLib.Object, GXml.Serializable {
return;
}
- public void set_property (GLib.ParamSpec spec, GLib.Value value) {
+ public new void set_property (GLib.ParamSpec spec, GLib.Value value)
+ {
switch (spec.name) {
case "private-field":
this.private_field = value.get_int ();
@@ -230,7 +264,74 @@ public class SerializableBanana : GLib.Object, GXml.Serializable {
}
}
-class SerializableTest : GXmlTest {
+class XmlObjectModel : Object, Serializable
+{
+ /* Serializable interface properties */
+ public GLib.HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get; protected set; }
+ public bool serializable_property_use_blurb { get; set; }
+ public GXml.Element serialized_xml_node { get; protected set; }
+ public string serialized_xml_node_value { get; protected set; }
+ public GLib.HashTable<string,GXml.DomNode> unknown_serializable_property { get; protected set; }
+
+ /* No serializable properties */
+ public string @value { get; set; }
+ public XmlObjectModel ()
+ {
+ serializable_property_use_blurb = true;
+ var pvalue = find_property_spec ("value");
+ ignored_serializable_properties.set ("value", pvalue);
+ }
+
+ public string to_string ()
+ {
+ var lp = list_serializable_properties ();
+ string ret = this.get_type ().name () +"{Properties:\n";
+ foreach (ParamSpec p in lp) {
+ ret += @"[$(p.name)]{" + get_property_value (p) + "}\n";
+ }
+ return ret + "}";
+ }
+
+ public static bool equals (Object a, Object b)
+ requires ((a is Serializable) && (b is Serializable))
+ {
+ if (b.get_type () == a.get_type ()) {
+ var alp = ((Serializable)a).list_serializable_properties ();
+ var blp = ((Serializable)b).list_serializable_properties ();
+ bool ret = true;
+ foreach (ParamSpec p in alp) {
+ var bp = ((Serializable)b).find_property_spec (p.name);
+ if (bp != null) {
+ var apval = ((Serializable)a).get_property_value (p);
+ var bpval = ((Serializable)b).get_property_value (bp);
+ if ( apval != bpval)
+ ret = false;
+ }
+ }
+ return ret;
+ }
+ return false;
+ }
+}
+
+class Laptop : XmlObjectModel
+{
+ public string manufacturer { get; set; }
+ public string model { get; set; }
+ public int cores { get; set; }
+ public float ghz { get; set; }
+
+ public Laptop ()
+ {
+ manufacturer = "MexicanLaptop, Inc.";
+ model = "LQ59678";
+ cores = 8;
+ ghz = (float) 3.5;
+ }
+}
+
+class SerializableTest : GXmlTest
+{
public static void add_tests () {
Test.add_func ("/gxml/serializable/interface_defaults", () => {
SerializableTomato tomato = new SerializableTomato (0, 0, 12, "cats");
@@ -291,5 +392,9 @@ class SerializableTest : GXmlTest {
SerializationTest.test_serialization_deserialization (banana,
"interface_override_properties", (GLib.EqualFunc)SerializableBanana.equals,
(SerializationTest.StringifyFunc)SerializableBanana.to_string);
});
+ Test.add_func ("/gxml/serializable/xml_object_model/derived_class", () => {
+ var lap = new Laptop ();
+ SerializationTest.test_serialization_deserialization (lap, "derived_class",
(GLib.EqualFunc)XmlObjectModel.equals, (SerializationTest.StringifyFunc)XmlObjectModel.to_string);
+ });
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]