[gxml/serialization: 171/172] Implemented proof of concept of SerializabelJson and SerializableObjectModel * Serializable methods



commit 2a2b8fcad86a7f5f73a2dc03492aa42af423a29e
Author: Daniel Espinosa <esodan gmail com>
Date:   Wed Oct 9 15:31:07 2013 -0500

    Implemented proof of concept of SerializabelJson and SerializableObjectModel
    * Serializable methods has been implemented by SerializableJson
    * SerializableJson implements Serializable interface
    * SerializableObjectModel implements Serializable interface
    * Serializable interface defined to use both or any future serialization method
    * Serializable spects to add nodes to a given Node, if is Document a root
    if an Element childs Nodes.
    * Serializable can be used to serialize objects implementing it

 gxml/Makefile.am                  |    2 +
 gxml/Serializable.vala            |  329 ++++++++++++++++++++++++++++++++-----
 gxml/SerializableJson.vala        |  213 ++++++++++++++++++++++++
 gxml/SerializableObjectModel.vala |  200 ++++++++++++++++++++++
 4 files changed, 701 insertions(+), 43 deletions(-)
---
diff --git a/gxml/Makefile.am b/gxml/Makefile.am
index 8f6d972..bb40117 100644
--- a/gxml/Makefile.am
+++ b/gxml/Makefile.am
@@ -60,6 +60,8 @@ libgxml_la_SOURCES = \
        ProcessingInstruction.vala \
        Text.vala \
        Serializable.vala \
+       SerializableObjectModel.vala \
+       SerializableJson.vala \
        Serialization.vala \
        $(NULL)
 
diff --git a/gxml/Serializable.vala b/gxml/Serializable.vala
index 53ac7fc..b21a2f9 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>
  */
 
 
@@ -70,39 +72,43 @@ namespace GXml {
         */
        public interface Serializable : GLib.Object {
                /**
-                * Handles deserializing individual properties.
+                * Defines the way to set Node name.
                 *
-                * Interface method to handle deserialization of an
-                * individual property.  The implementing class
-                * receives a description of the property and the
-                * { link GXml.Node} that contains the content.  The
-                * implementing { link GXml.Serializable} object can extract
-                * the data from the { link GXml.Node} and store it in its
-                * property itself. Note that the { link GXml.Node} may be
-                * as simple as a { link GXml.Text} that stores the data as a
-                * string.
+                * By default is set to object's type's name lowercase.
                 *
-                * If the implementation has handled deserialization,
-                * return true.  Return false if you want
-                * { link GXml.Serialization} to try to automatically
-                * deserialize it.  If { link GXml.Serialization} tries to
-                * handle it, it will want either { link GXml.Serializable}'s
-                * set_property (or at least { link GLib.Object.set_property})
-                * to know about the property.
+                * This property must be ignored on serialisation.
+                */
+               public abstract string serializable_node_name { get; protected set; }
+
+               public abstract bool serializable_property_use_nick { get; set; }
+               /**
+                * Store all properties to be ignored on serialization.
                 *
-                * @param property_name the name of the property as a string
-                * @param spec the { link GLib.ParamSpec} describing the property.
-                * @param property_node the { link GXml.Node} encapsulating data to deserialize
-                * @return `true` if the property was handled, `false` if { link GXml.Serialization} should 
handle it.
+                * Implementors: By default { link list_serializable_properties} initialize
+                * this property to store all public properties, except this one.
                 */
-               /*
-                * @todo: consider not giving property_name, but
-                * letting them get name from spec
-                * @todo: consider returning { link GLib.Value} as out param
+               public abstract HashTable<string,GLib.ParamSpec>  ignored_serializable_properties { get; 
protected set; }
+               /**
+                * On deserialization stores any { link Node} not used on this
+                * object, but exists in current XML file.
+                *
+                * This property must be ignored on serialisation.
                 */
-               public virtual bool deserialize_property (string property_name, /* out GLib.Value value,*/ 
GLib.ParamSpec spec, GXml.Node property_node) {
-                       return false; // default deserialize_property gets used
-               }
+               public abstract HashTable<string,GXml.Node>    unknown_serializable_property { get; protected 
set; }
+
+               /**
+                * Used by to add content in an { link GXml.Element}.
+                *
+                * This property must be ignored on serialisation.
+                */
+               public abstract string?  serialized_xml_node_value { get; protected set; default = null; }
+
+               /**
+                * Serialize this object.
+                *
+                * @doc an { link GXml.Document} object to serialise to 
+                */
+               public abstract Node? serialize (Node node);
 
                /**
                 * Handles serializing individual properties.
@@ -126,15 +132,70 @@ namespace GXml {
                 * @param doc the { link GXml.Document} the returned { link GXml.Node} should belong to
                 * @return a new { link GXml.Node}, or `null`
                 */
-               /*
-                * @todo: consider not giving property_name, let them get name from spec?
+               public abstract GXml.Node? serialize_property (Element element,
+                                                              GLib.ParamSpec prop);
+
+               /**
+                * Deserialize this object.
+                *
+                * @node { link GXml.Node} used to deserialize from.
                 */
-               public virtual GXml.Node? serialize_property (string property_name, /*GLib.Value value, */ 
GLib.ParamSpec spec, GXml.Document doc) {
-                       return null; // default serialize_property gets used
-               }
+               public abstract Node? deserialize (Node node)
+                                                 throws SerializableError;
+               /**
+                * Handles deserializing individual properties.
+                *
+                * Interface method to handle deserialization of an
+                * individual property.  The implementing class
+                * receives a description of the property and the
+                * { link GXml.Node} that contains the content.  The
+                * implementing { link GXml.Serializable} object can extract
+                * the data from the { link GXml.Node} and store it in its
+                * property itself. Note that the { link GXml.Node} may be
+                * as simple as a { link GXml.Text} that stores the data as a
+                * string.
+                *
+                * @param property_name the name of the property as a string
+                * @param spec the { link GLib.ParamSpec} describing the property.
+                * @param property_node the { link GXml.Node} encapsulating data to deserialize
+                * @return `true` if the property was handled, `false` if { link GXml.Serialization} should 
handle it.
+                */
+               public abstract bool deserialize_property (GXml.Node property_node)
+                                                         throws SerializableError;
+
+               /**
+                * Signal to serialize unknown properties.
+                * 
+                * @element a { link GXml.Node} to add attribute or child nodes to
+                * @prop a { link GLib.ParamSpec} describing attribute to serialize
+                * @node set to the { link GXml.Node} representing this attribute
+                */
+               public signal void serialize_unknown_property (Node element, ParamSpec prop, out Node node);
 
-               /* Correspond to: g_object_class_{find_property,list_properties} */
+               /**
+                * Signal to serialize unknown properties.
+                * 
+                * @element a { link GXml.Node} to add attribute or child nodes to
+                * @prop a { link GLib.ParamSpec} describing attribute to serialize
+                * @node set to the { link GXml.Node} representing this attribute
+                */
+               public signal void serialize_unknown_property_type (Node element, ParamSpec prop, out Node 
node);
+
+               /**
+                * Signal to deserialize unknown properties.
+                *
+                * @node a { link GXml.Node} to get attribute from
+                * @prop a { link GLib.ParamSpec} describing attribute to deserialize
+                */
+               public signal void deserialize_unknown_property (Node node, ParamSpec prop);
 
+               /**
+                * Signal to deserialize unknown properties' type.
+                *
+                * @node a { link GXml.Node} to get attribute from
+                * @prop a { link GLib.ParamSpec} describing attribute to deserialize
+                */
+               public signal void deserialize_unknown_property_type (Node node, ParamSpec prop);
                /*
                 * Handles finding the { link GLib.ParamSpec} for a given property.
                 *
@@ -163,8 +224,39 @@ 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 ();
+                       string pn = property_name.down ();
+                       if (ignored_serializable_properties.contains (pn)) {
+                               return null;
+                       }
+                       return get_class ().find_property (pn);
+               }
+
+               /**
+                * 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-value",
+                                                                    get_class 
().find_property("serialized-xml-node-value"));
+                               ignored_serializable_properties.set ("serializable-property-use-nick",
+                                                                    get_class 
().find_property("serializable-property-use-nick"));
+                               ignored_serializable_properties.set ("serializable-node-name",
+                                                                    get_class 
().find_property("serializable-node-name"));
+                       }
+                       if (unknown_serializable_property == null) {
+                               unknown_serializable_property = new HashTable<string,GXml.Node> (str_hash, 
str_equal);
+                       }
                }
 
                /*
@@ -195,8 +287,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 +328,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 +365,139 @@ 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);
+                       }
+               }
+                               /* TODO:
+                * - can't seem to pass delegates on struct methods to another function :(
+                * - no easy string_to_gvalue method in GValue :(
+                */
+
+               /**
+                * Transforms a string into another type hosted by { link GLib.Value}.
+                *
+                * A utility function that handles converting a string
+                * representation of a value into the type specified by the
+                * supplied #GValue dest.  A #GXmlSerializableError will be
+                * set if the string cannot be parsed into the desired type.
+                *
+                * @param str the string to transform into the given #GValue object
+                * @param dest the #GValue out parameter that will contain the parsed value from the string
+                * @return `true` if parsing succeeded, otherwise `false`
+                */
+               /*
+                * @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 SerializableError
+               {
+                       Type t = dest.type ();
+                       GLib.Value dest2 = Value (t);
+                       bool ret = false;
+
+                       if (t == typeof (int64)) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_int64 (val);
+                               }
+                       } else if (t == typeof (int)) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_int ((int)val);
+                               }
+                       } else if (t == typeof (long)) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_long ((long)val);
+                               }
+                       } else if (t == typeof (uint)) {
+                               uint64 val;
+                               if (ret = uint64.try_parse (str, out val)) {
+                                       dest2.set_uint ((uint)val);
+                               }
+                       } else if (t == typeof (ulong)) {
+                               uint64 val;
+                               if (ret = uint64.try_parse (str, out val)) {
+                                       dest2.set_ulong ((ulong)val);
+                               }
+                       } else if ((int)t == 20) { // gboolean
+                               bool val = (str == "TRUE");
+                               dest2.set_boolean (val); // TODO: huh, investigate why the type is gboolean 
and not bool coming out but is going in
+                               ret = true;
+                       } else if (t == typeof (bool)) {
+                               bool val;
+                               if (ret = bool.try_parse (str, out val)) {
+                                       dest2.set_boolean (val);
+                               }
+                       } else if (t == typeof (float)) {
+                               double val;
+                               if (ret = double.try_parse (str, out val)) {
+                                       dest2.set_float ((float)val);
+                               }
+                       } else if (t == typeof (double)) {
+                               double val;
+                               if (ret = double.try_parse (str, out val)) {
+                                       dest2.set_double (val);
+                               }
+                       } else if (t == typeof (string)) {
+                               dest2.set_string (str);
+                               ret = true;
+                       } else if (t == typeof (char)) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_char ((char)val);
+                               }
+                       } else if (t == typeof (uchar)) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_uchar ((uchar)val);
+                               }
+                       } else if (t == Type.BOXED) {
+                       } else if (t.is_enum ()) {
+                               int64 val;
+                               if (ret = int64.try_parse (str, out val)) {
+                                       dest2.set_enum ((int)val);
+                               }
+                       } else if (t.is_flags ()) {
+                       } else if (t.is_object ()) {
+                       } else {
+                       }
+
+                       if (ret == true) {
+                               dest = dest2;
+                               return true;
+                       } else {
+                               throw new SerializableError.UNSUPPORTED_TYPE ("%s/%s", t.name (), t.to_string 
());
+                       }
                }
+
+               public static string gvalue_to_string (GLib.Value val)
+                                                    throws SerializableError
+               {
+                       Value ret = "";
+                       if (Value.type_transformable (val.type (), typeof (string)))
+                       {
+                               val.transform (ref ret);
+                               return ret.dup_string ();
+                       }
+                       else
+                       {
+                               throw new SerializableError.UNSUPPORTED_TYPE ("Can't transform '%s' to 
string", val.type ().name ());
+                       }
+               }
+       }
+
+       /**
+        * Errors from { link Serialization}.
+        */
+       public errordomain SerializableError {
+               /**
+                * An object with a known { link GLib.Type} that we do not support was encountered.
+                */
+               UNSUPPORTED_TYPE
        }
 }
diff --git a/gxml/SerializableJson.vala b/gxml/SerializableJson.vala
new file mode 100644
index 0000000..a207650
--- /dev/null
+++ b/gxml/SerializableJson.vala
@@ -0,0 +1,213 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Serialization.vala
+ *
+ * Copyright (C) 2012-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 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:
+ *       Richard Schwarting <aquarichy gmail com>
+ *       Daniel Espinosa <esodan gmail com>
+ */
+
+/**
+ * Serializes and deserializes { link GLib.Object}s to and from
+ * { link GXml.Node}.
+ *
+ * Serialization can automatically serialize a variety of public
+ * properties.  { link GLib.Object}s can also implement the
+ * { link GXml.Serializable} to partially or completely manage
+ * serialization themselves, including non-public properties or
+ * data types not automatically supported by { link GXml.Serialization}.
+ */
+public class GXml.SerializableJason : GLib.Object, Serializable
+{
+       /* Serializable Interface properties */
+       public string serializable_node_name { get; protected set; }
+       public bool serializable_property_use_nick { get; set; }
+       public HashTable<string,GLib.ParamSpec>  ignored_serializable_properties { get; protected set; }
+       public HashTable<string,GXml.Node>    unknown_serializable_property { get; protected set; }
+
+       public string?  serialized_xml_node_value { get; protected set; default = null; }
+
+  /**
+   * If @node is a Document serialize just add an <Object> element.
+   *
+   * If @node is an Element serialize add to it an <Object> element.
+   *
+   * Is up to you to add convenient Element node to a Document, in order to be
+   * used by serialize and add new <Object> tags per object to serialize.
+   */
+       public Node? serialize (Node node)
+       {
+               Document doc;
+               Element root;
+               ParamSpec[] prop_specs;
+               Element prop;
+               Node value_prop = null;
+               string oid = "%p".printf(this);
+
+               if (node is Document)
+                       doc = (Document) node;
+               else
+                       doc = node.owner_document;
+
+               root = doc.create_element ("Object");
+               doc.append_child (root);
+               root.set_attribute ("otype", object.get_type ().name ());
+               root.set_attribute ("oid", oid);
+
+               prop_specs = serializable.list_properties ();
+
+               foreach (ParamSpec prop_spec in prop_specs) {
+                       prop = doc.create_element ("Property");
+                       prop.set_attribute ("ptype", prop_spec.value_type.name ());
+                       prop.set_attribute ("pname", prop_spec.name);
+                       value_prop = serialize_property (prop, prop_spe);
+                       prop.append_child (value_prop);
+                       root.append_child (prop);
+               }
+               return root;
+       }
+
+       public GXml.Node? serialize_property (Element element,
+                                                      GLib.ParamSpec prop)
+       {
+               Type type;
+               Value value;
+               Node value_node = null;
+               
+               type = prop.value_type;
+
+               if (type.is_a (typeof (Serializable)))
+                       return (Serializable).serialize ();
+
+               var doc = element.owner_document;
+
+               if (type.is_enum ())
+               {
+                       value = Value (typeof (int));
+                       this.get_property (prop_spec.name, ref value);
+                       value_node = doc.create_text_node ("%d".printf (value.get_int ()));
+               } 
+               else if (Value.type_transformable (type, typeof (string))) 
+               { // e.g. int, double, string, bool
+                       value = Value (typeof (string));
+                       this.get_property (prop_spec.name, ref value);
+                       value_node = doc.create_text_node (value.get_string ());
+               }
+               else if (type == typeof (GLib.Type)) {
+                       value_node = doc.create_text_node (type.name ());
+               }
+               else if (type.is_a (typeof (GLib.Object))
+                          && ! type.is_a (typeof (Gee.Collection)))
+               {
+                       GLib.Object child_object;
+
+                       // TODO: this is going to get complicated
+                       value = Value (typeof (GLib.Object));
+                       this.get_property (prop.name, ref value);
+                       child_object = value.get_object ();
+                       Document value_doc = Serialization.serialize_object (child_object);
+
+                       value_node = doc.copy_node (value_doc.document_element);
+               }
+               else if (type.name () == "gpointer") {
+                       GLib.warning ("DEBUG: skipping gpointer with name '%s' of object '%s'", 
prop_spec.name, object.get_type ().name ());
+                       value_node = doc.create_text_node (prop.name);
+               } else {
+                       throw new SerializationError.UNSUPPORTED_TYPE ("Can't currently serialize type '%s' 
for property '%s' of object '%s'", type.name (), prop.name, object.get_type ().name ());
+               }
+
+               return value_node;
+       }
+
+       public Node? deserialize (Node node) throws SerializableError
+       {
+               bool ret = true;
+               Element obj_elem;
+               string otype;
+               string oid;
+               Type type;
+               ParamSpec[] specs;
+
+               if (node is Document) {
+                       obj_elem = node.owner_document.document_element;
+               }
+               else {
+                       obj_elem = (Element) node;
+               }
+
+               specs = this.list_serializable_properties ();
+
+               foreach (Node child_node in obj_elem.child_nodes) {
+                       deserialize_property (child_node);
+               }
+               return obj_elem;
+       }
+
+       public bool deserialize_property (GXml.Node property_node) throws SerializableError
+       {
+               if (properly_node.node_name == "Property")
+               {
+                       Element prop_elem;
+                       string pname;
+                       Value val;
+                       //string ptype;
+
+                       prop_elem = (Element)child_node;
+                       pname = prop_elem.get_attribute ("pname");
+                       //ptype = prop_elem.get_attribute ("ptype"); // optional
+
+                       // Check name and type for property
+                       spec = this.find_property_spec (pname);
+
+                       if (spec == null) {
+                               GLib.message ("Deserializing object of type '%s' claimed unknown property 
named '%s'\nXML [%s]", otype, pname, obj_elem.to_string ());
+                               unknown_serializable_property.set (property_node.node_name, property_node);
+                               continue;
+                       }
+                       else {
+                               if (spec.value_type ().is_a (typeof(Serializable)))
+                               {
+                                       Value vobj;
+                                       this.get_property (pname, out vobj);
+                                       ((Serializable) vobj).deserialize (child_node);
+                               }
+                               else {
+                                       val = Value (type);
+                                       if (GLib.Value.type_transformable (type, typeof (string))) {
+                                               Serializable.string_to_gvalue (prop_elem.content, ref val);
+                                               this.set_property_value (pname, val);
+                                       }
+                                       else if (type.is_a (typeof (GLib.Object))) 
+                                       {
+                                               GXml.Node prop_elem_child;
+                                               Object property_object;
+
+                                               prop_elem_child = prop_elem.first_child;
+
+                                               property_object = Serialization.deserialize_object_from_node 
(prop_elem_child);
+                                               val.set_object (property_object);
+                                       }
+                                       else {
+                                               deserialize_unknown_property_type.set (prop_elem, spec);
+                                               ret = false;
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/gxml/SerializableObjectModel.vala b/gxml/SerializableObjectModel.vala
new file mode 100644
index 0000000..b554154
--- /dev/null
+++ b/gxml/SerializableObjectModel.vala
@@ -0,0 +1,200 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* ObjectModel.vala
+ *
+ * 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
+ * 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 abstract class GXml.SerializableObjectModel : Object, Serializable
+{
+       /* Serializable interface properties */
+       public GLib.HashTable<string,GLib.ParamSpec> ignored_serializable_properties { get; protected set; }
+       public bool serializable_property_use_nick { get; set; }
+       public string? serialized_xml_node_value { get; protected set; default=null; }
+       public GLib.HashTable<string,GXml.Node> unknown_serializable_property { get; protected set; }
+       public string serializable_node_name { get; protected set; }
+
+       public SerializableObjectModel ()
+       {
+               serializable_property_use_nick = true;
+               serialized_xml_node_value = null;
+               serializable_node_name = get_type().name().down();
+       }
+
+       public Node? serialize (Node node)
+       {
+               Document doc;
+               if (node is Document)
+                       doc = (Document) node;
+               else
+                       doc = node.owner_document;
+               GLib.message ("Serialing on ..." + node.node_name);
+               var element = doc.create_element (serializable_node_name);
+               node.append_child (element);
+               if (serialized_xml_node_value != null)
+                       element.content = serialized_xml_node_value;
+               GLib.message ("Node Value is: ?" + element.content);
+               foreach (ParamSpec spec in list_serializable_properties ()) {
+                       GLib.message ("Property to Serialize: " + spec.name);
+                       serialize_property (element, spec);
+               }
+               GLib.message ("Added a new top node: " + element.node_name);
+               return element;
+       }
+       
+       public GXml.Node? serialize_property (Element element,
+                                             GLib.ParamSpec prop)
+       {
+               if (prop.value_type.is_a (typeof (Serializable))) 
+               {
+                       GLib.message (@"$(prop.name) Is a Serializable");
+                       var v = Value (typeof (Object));
+                       get_property (prop.name, ref v);
+                       var obj = (Serializable) v.get_object ();
+                       return obj.serialize (element);
+               }
+               Node node = null;
+               Value oval = Value (prop.value_type);
+               get_property (prop.name, ref oval);
+               string val = "";
+               if (Value.type_transformable (prop.value_type, typeof (string)))
+               {
+                       Value rval = Value (typeof (string));
+                       oval.transform (ref rval);
+                       val = rval.dup_string ();
+                       string attr_name = prop.name.down ();
+                       var attr = element.get_attribute_node (attr_name);
+                       if (attr == null) {
+                               GLib.message (@"New Attr to add... $(attr_name)");
+                               element.set_attribute (attr_name, val);
+                       }
+                       else
+                               attr.value = val;
+                       return (Node) attr;
+               }
+               this.serialize_unknown_property (element, prop, out node);
+               return node;
+       }
+       
+       public virtual Node? deserialize (Node node)
+                                         throws SerializableError
+       {
+               Document doc;
+               if (node is Document) {
+                       doc = (Document) node;
+                       return_val_if_fail (doc.document_element != null, null);
+               }
+               else
+                       doc = node.owner_document;
+               Element element;
+               if (node is Element)
+                       element = (Element) node;
+               else
+                       element = (Element) doc.document_element;
+               return_val_if_fail (element.node_name.down () == serializable_node_name, null);
+               foreach (Attr attr in element.attributes.get_values ())
+               {
+                       GLib.message (@"Deseralizing Attribute: $(attr.name)");
+                       deserialize_property (attr);
+               }
+               if (element.has_child_nodes ())
+               {
+                       GLib.message ("Have child Elements ...");
+                       foreach (Node n in element.child_nodes)
+                       {
+                               GLib.message (@"Deseralizing Element: $(n.node_name)");
+                               deserialize_property (n);
+                       }
+               }
+               if (element.content != null)
+                               serialized_xml_node_value = element.content;
+               return null;
+       }
+
+       public virtual bool deserialize_property (GXml.Node property_node)
+                                                 throws SerializableError
+       {
+               bool ret = false;
+               var prop = find_property_spec (property_node.node_name);
+               if (prop == null) {
+                       GLib.message ("Found Unknown property: " + property_node.node_name);
+                       // FIXME: Event emit
+                       unknown_serializable_property.set (property_node.node_name, property_node);
+                       return true;
+               }
+               if (prop.value_type.is_a (typeof (Serializable)))
+               {
+                       GLib.message (@"$(prop.name): Is Serializable...");
+                       Value vobj = Value (typeof(Object));
+                       get_property (prop.name, ref vobj);
+                       if (vobj.get_object () == null) {
+                               var obj = Object.new  (prop.value_type);
+                               ((Serializable) obj).deserialize (property_node);
+                               set_property (prop.name, obj);
+                       }
+                       else
+                               ((Serializable) vobj.get_object ()).deserialize (property_node);
+                       return true;
+               }
+               else {
+                       Value val = Value (prop.value_type);
+                       if (Value.type_transformable (typeof (Node), prop.value_type))
+                       {
+                               Value tmp = Value (typeof (Node));
+                               tmp.set_object (property_node);
+                               ret = tmp.transform (ref val);
+                               set_property (prop.name, val);
+                               return ret;
+                       }
+                       if (property_node is GXml.Attr)
+                       {
+                               Value ptmp = Value (typeof (string));
+                               ptmp.set_string (property_node.node_value);
+                               if (Value.type_transformable (typeof (string), prop.value_type))
+                                       ret = ptmp.transform (ref val);
+                               else
+                                       ret = string_to_gvalue (property_node.node_value, ref val);
+                               set_property (prop.name, val);
+                               return ret;
+                       }
+               }
+               // Attribute can't be deseralized with standard methods. Up to the implementor.
+               this.deserialize_unknown_property (property_node, prop);
+               return true;
+       }
+       public abstract string to_string ();
+
+       public static bool equals (SerializableObjectModel a, SerializableObjectModel b)
+       {
+               if (b.get_type () == a.get_type ()) {
+                       var alp = ((Serializable)a).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;
+       }
+}


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