[ease/serialize: 3/52] Serializer sort of working.
- From: Nate Stedman <natesm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ease/serialize: 3/52] Serializer sort of working.
- Date: Tue, 22 Feb 2011 22:57:01 +0000 (UTC)
commit cc305b68ab33d8554822db1861d2e1b0cb9aebfb
Author: Nate Stedman <natesm gmail com>
Date: Fri Nov 26 22:49:10 2010 -0500
Serializer sort of working.
ease-core/ease-color.vala | 12 +-
ease-core/ease-document.vala | 31 +++-
ease-core/ease-element.vala | 14 +-
ease-core/ease-serializable.vala | 64 ++++++
ease-core/ease-serializer.vala | 428 ++++++++++++++++++++++++++++++++++++--
ease-core/ease-slide.vala | 18 ++-
ease/ease-editor-window.vala | 3 -
ease/ease-main.vala | 3 +
8 files changed, 539 insertions(+), 34 deletions(-)
---
diff --git a/ease-core/ease-color.vala b/ease-core/ease-color.vala
index fe4fba8..c6b0cf3 100644
--- a/ease-core/ease-color.vala
+++ b/ease-core/ease-color.vala
@@ -18,7 +18,7 @@
/**
* Color abstraction, supporting Clutter, GDK, and Cairo colors.
*/
-public class Ease.Color : GLib.Object
+public class Ease.Color : GLib.Object, Serializable
{
/**
* The format string for converting Colors to strings.
@@ -356,6 +356,16 @@ public class Ease.Color : GLib.Object
}
/**
+ * Properties that should be excluded from serialization.
+ */
+ public string[] serialize_exclude()
+ {
+ return { "red8", "green8", "blue8", "alpha8",
+ "red16", "green16", "blue16", "alpha16",
+ "clutter", "gdk" };
+ }
+
+ /**
* Creates an opaque color.
*/
public Color.rgb(double r, double g, double b)
diff --git a/ease-core/ease-document.vala b/ease-core/ease-document.vala
index 78ec4a3..7a9492f 100644
--- a/ease-core/ease-document.vala
+++ b/ease-core/ease-document.vala
@@ -21,7 +21,7 @@
* The Ease Document class is generated from JSON and writes back to JSON
* when saved.
*/
-public class Ease.Document : GLib.Object, UndoSource
+public class Ease.Document : GLib.Object, UndoSource, Serializable
{
private const string MEDIA_PATH = "Media";
@@ -110,10 +110,32 @@ public class Ease.Document : GLib.Object, UndoSource
*/
public string path { get; set; }
+ /**
+ * The properties that should be excluded from serialization.
+ */
public string[] serialize_exclude()
{
return { "path", "filename", "aspect", "length" };
}
+
+ /**
+ * Serializes the ListStore of slides.
+ */
+ public bool serialize_custom(string property, Json.Object object)
+ {
+ if (property != "slides") return false;
+
+ var array = new Json.Array();
+ Slide s;
+ foreach (var iter in slides)
+ {
+ slides.get(iter, COL_SLIDE, out s);
+ array.add_object_element(Serializer.write(s));
+ }
+ object.set_array_member(property, array);
+
+ return true;
+ }
/**
* All { link Slide}s in this Document.
@@ -216,7 +238,12 @@ public class Ease.Document : GLib.Object, UndoSource
slide.parent = this;
append_slide(slide);
- Serializer.write(this);
+ var gen = new Json.Generator();
+ var root = new Json.Node(Json.NodeType.OBJECT);
+ root.set_object(Serializer.write(this));
+ gen.root = root;
+ gen.pretty = true;
+ gen.to_file("/home/nate/Desktop/test.json");
}
public void to_json(Gtk.Window? window) throws GLib.Error
diff --git a/ease-core/ease-element.vala b/ease-core/ease-element.vala
index 9777a50..d7e33db 100644
--- a/ease-core/ease-element.vala
+++ b/ease-core/ease-element.vala
@@ -22,7 +22,7 @@
* abstract, so each type of element is represented by a subclass. The Element
* base class contains properties common to all types of element.
*/
-public abstract class Ease.Element : GLib.Object, UndoSource
+public abstract class Ease.Element : GLib.Object, UndoSource, Serializable
{
/**
* The default width of { link Theme} master slides.
@@ -64,6 +64,14 @@ public abstract class Ease.Element : GLib.Object, UndoSource
internal Document document { get { return parent.parent; } }
/**
+ * Properties to exclude from serialization.
+ */
+ public string[] serialize_exclude()
+ {
+ return { "parent", "document" };
+ }
+
+ /**
* Notifies of a position change for this Element. Note that the "x" and "y"
* properties do not emit the "changed" signal - only this signal.
*/
@@ -294,10 +302,6 @@ public abstract class Ease.Element : GLib.Object, UndoSource
*/
public string identifier { get; set; }
- /**
- * The Element's type: currently "text", "image", or "video".
- */
- public string element_type { get; set; }
/**
* The X position of this Element.
diff --git a/ease-core/ease-serializable.vala b/ease-core/ease-serializable.vala
index 3354438..7c7a917 100644
--- a/ease-core/ease-serializable.vala
+++ b/ease-core/ease-serializable.vala
@@ -18,4 +18,68 @@
public interface Ease.Serializable
{
public abstract string[] serialize_exclude();
+
+ /**
+ * Simply combines two arrays, for use with serialize_exclude.
+ */
+ public string[] serialize_combine(string[] one, string[] two)
+ {
+ string[] combined = new string[one.length + two.length];
+
+ for (int i = 0; i < one.length; i++)
+ {
+ if (i < one.length) combined[i] = one[i];
+ else combined[i] = two[i - one.length];
+ }
+
+ return combined;
+ }
+
+ /**
+ * Triggers the serialize_custom function properly for subclasses.
+ */
+ internal bool serialize_custom_run(string property, Json.Object object)
+ {
+ return serialize_custom(property, object);
+ }
+
+ /**
+ * Allows custom serialization for specific properties that cannot be
+ * serialized in the typical way.
+ *
+ * If the property was serialized in a custom manner, the function should
+ * return true, otherwise false. By default, this function simply returns
+ * false.
+ *
+ * @param property The property to serialize.
+ * @param object The JSON object to modify.
+ */
+ public virtual bool serialize_custom(string property, Json.Object object)
+ {
+ return false;
+ }
+
+ /**
+ * Triggers the serialize_custom function properly for subclasses.
+ */
+ internal bool deserialize_custom_run(string property, Json.Object object)
+ {
+ return deserialize_custom(property, object);
+ }
+
+ /**
+ * Allows custom deserialization for specific properties that cannot be
+ * deserialized in the typical way.
+ *
+ * If the property was deserialized in a custom manner, the function should
+ * return true, otherwise false. By default, this function simply returns
+ * false.
+ *
+ * @param property The property to deserialize.
+ * @param object The JSON object to read from.
+ */
+ public virtual bool deserialize_custom(string property, Json.Object object)
+ {
+ return false;
+ }
}
diff --git a/ease-core/ease-serializer.vala b/ease-core/ease-serializer.vala
index 9c2aaf3..f5f83b5 100644
--- a/ease-core/ease-serializer.vala
+++ b/ease-core/ease-serializer.vala
@@ -17,38 +17,426 @@
public static class Ease.Serializer
{
- private const string SCRIPT = """
- for (i in object.get_class().get_properties())
+ private static Gee.TreeMap<GLib.Type, CollectionBox> collections;
+
+ private const string TYPE_KEY = "__EASE_SERIALIZER_OBJECT_TYPE_NAME__";
+
+ public static Json.Object write(GLib.Object object)
{
- print(i);
+ // create the json object
+ var json = new Json.Object();
+
+ // store the object's type in the reserved key
+ json.set_string_member(TYPE_KEY, object.get_type().name());
+
+ // check for any excluded properties
+ string[] exclude = {};
+ if (object is Serializable)
+ {
+ exclude = (object as Serializable).serialize_exclude();
+ }
+
+ // iterate and serialize properties
+ foreach (var pspec in object.get_class().list_properties())
+ {
+ // don't serialize excluded types
+ if (pspec.name in exclude) continue;
+
+ // if the object implements Serializable, allow custom behavior
+ if (object is Serializable)
+ {
+ if ((object as Serializable).serialize_custom_run(pspec.name,
+ json))
+ continue;
+ }
+
+ // get the object/not object as a value
+ GLib.Value val = GLib.Value(pspec.value_type);
+ object.get_property(pspec.name, ref val);
+
+ // if the object is a registered collection, turn it into an array
+ if (collections != null)
+ {
+ var type = pspec.value_type;
+ bool iterated = false;
+ do
+ {
+ CollectionIterator? iterator = null;
+ if ((iterator = find_iterator(type)) != null)
+ {
+ var array = new Json.Array();
+ iterator((GLib.Object)val, array);
+ json.set_array_member(pspec.name, array);
+ iterated = true;
+ break;
+ }
+ } while (0 != (type = type.parent()));
+
+ if (iterated) continue;
+ }
+
+ // serialize GObjects
+ if (Value.type_transformable(pspec.value_type, typeof(Object)))
+ {
+ json.set_object_member(pspec.name, write((Object)val));
+ }
+
+ // serialize basic types
+ if (pspec.value_type == typeof(string))
+ {
+ json.set_string_member(pspec.name, (string)val); continue;
+ }
+
+ if (pspec.value_type == typeof(bool))
+ {
+ json.set_boolean_member(pspec.name, (bool)val); continue;
+ }
+
+ if (pspec.value_type == typeof(char))
+ {
+ json.set_int_member(pspec.name, (char)val); continue;
+ }
+
+ if (pspec.value_type == typeof(uchar))
+ {
+ json.set_int_member(pspec.name, (uchar)val); continue;
+ }
+
+ if (pspec.value_type == typeof(int))
+ {
+ json.set_int_member(pspec.name, (int)val); continue;
+ }
+
+ if (pspec.value_type == typeof(uint))
+ {
+ json.set_int_member(pspec.name, (uint)val); continue;
+ }
+
+ if (pspec.value_type == typeof(long))
+ {
+ json.set_int_member(pspec.name, (long)val); continue;
+ }
+
+ if (pspec.value_type == typeof(ulong))
+ {
+ json.set_int_member(pspec.name, (ulong)val); continue;
+ }
+
+ if (pspec.value_type == typeof(size_t))
+ {
+ json.set_int_member(pspec.name, (size_t)val); continue;
+ }
+
+ if (pspec.value_type == typeof(ssize_t))
+ {
+ json.set_int_member(pspec.name, (ssize_t)val); continue;
+ }
+
+ if (pspec.value_type == typeof(int8))
+ {
+ json.set_int_member(pspec.name, (int8)val); continue;
+ }
+
+ if (pspec.value_type == typeof(uint8))
+ {
+ json.set_int_member(pspec.name, (uint8)val); continue;
+ }
+
+ /*if (pspec.value_type == typeof(int16))
+ {
+ json.set_int_member(pspec.name, (int16)val); continue;
+ }
+
+ if (pspec.value_type == typeof(uint16))
+ {
+ json.set_int_member(pspec.name, (uint16)val); continue;
+ }*/
+
+ if (pspec.value_type == typeof(int32))
+ {
+ json.set_int_member(pspec.name, (int32)val); continue;
+ }
+
+ if (pspec.value_type == typeof(uint32))
+ {
+ json.set_int_member(pspec.name, (uint32)val); continue;
+ }
+
+ if (pspec.value_type == typeof(int64))
+ {
+ json.set_int_member(pspec.name, (int64)val); continue;
+ }
+
+ if (pspec.value_type == typeof(float))
+ {
+ json.set_double_member(pspec.name, (float)val); continue;
+ }
+
+ if (pspec.value_type == typeof(double))
+ {
+ json.set_double_member(pspec.name, (double)val); continue;
+ }
+
+ if (pspec.value_type.is_enum())
+ {
+ json.set_int_member(pspec.name, (int)val); continue;
+ }
+
+ // uint64 won't fit in an int64, so make it a string to preserve
+ // the content (instead of just casting back and forth)
+ if (pspec.value_type == typeof(uint64))
+ {
+ json.set_string_member(pspec.name, "%lld".printf((uint64)val));
+ continue;
+ }
+ }
+
+ return json;
}
- """;
- public static void write(GLib.Object object)
+ public static GLib.Object read(Json.Object json)
{
- debug("Seed should happen now:");
- var context = Seed.Context.create(null, null);
- Seed.prepare_global_context(context);
+ // create the object with the type that TYPE_KEY specifies
+ var type = GLib.Type.from_name(json.get_string_member(TYPE_KEY));
+ var object = new GLib.Object(type, null);
- Seed.Object.set_property(context,
- context.get_global_object(),
- "object",
- Seed.Value.from_object(context, object, null));
+ // deserialize all properties
+ json.foreach_member((jobj, property, node) => {
+ // if the object implements Serializable, allow custom behavior
+ if (object is Serializable)
+ {
+ if ((object as Serializable).deserialize_custom_run(property,
+ jobj))
+ return;
+ }
+
+ // get a paramspec for the property to deserialize into
+ var pspec = object.get_class().find_property(property);
+
+ // create a GValue to serialize into
+ GLib.Value val = GLib.Value(pspec.value_type);
+
+ // deserialize GObjects
+ if (Value.type_transformable(pspec.value_type, typeof(Object)))
+ {
+ val = read(json.get_object_member(pspec.name));
+ }
+
+ // serialize basic types
+ else if (pspec.value_type == typeof(string))
+ {
+ val = json.get_string_member(property);
+ }
+
+ else if (pspec.value_type == typeof(bool))
+ {
+ val = json.get_boolean_member(property);
+ }
+
+ else if (pspec.value_type == typeof(char))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(uchar))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(int))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(uint))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(long))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(ulong))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(size_t))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(ssize_t))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(int8))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(uint8))
+ {
+ val = json.get_int_member(property);
+ }
+
+ /*if (pspec.value_type == typeof(int16))
+ {
+ val = json.get_int_member(property);
+ }
+
+ if (pspec.value_type == typeof(uint16))
+ {
+ val = json.get_int_member(property);
+ }*/
+
+ else if (pspec.value_type == typeof(int32))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(uint32))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(int64))
+ {
+ val = json.get_int_member(property);
+ }
+
+ else if (pspec.value_type == typeof(float))
+ {
+ val = json.get_double_member(property);
+ }
+
+ else if (pspec.value_type == typeof(double))
+ {
+ val = json.get_double_member(property);
+ }
+
+ else if (pspec.value_type.is_enum())
+ {
+ val = json.get_int_member(property);
+ }
+
+ object.set_property(property, val);
+ });
- unowned Seed.Script script = Seed.make_script(context, SCRIPT, null, 0);
+ return object;
+ }
+
+ /**
+ * Registers an iteration function for a collection type.
+ *
+ * When attempting to find a collection iterator, the serializer will search
+ * the main class first, then its parent class, etc. Interfaces implemented
+ * by each level will be searched first.
+ *
+ * @param type The type.
+ * @param callback The function to iterate over the type.
+ */
+ public static void register(GLib.Type type,
+ CollectionIterator iterator,
+ CollectionBuilder builder)
+ {
+ if (collections == null)
+ {
+ collections = new Gee.TreeMap<GLib.Type, CollectionBox>();
+ }
- Seed.Exception? e = null;
- if ((e = script.exception()) != null)
+ collections.set(type, new CollectionBox(iterator, builder));
+ }
+
+ /**
+ * Registers the builtin iterations functions.
+ */
+ public static void register_builtins()
+ {
+ // GList
+ register(typeof(GLib.List), (object, array) => {
+ foreach (var item in (GLib.List<GLib.Object>)object)
+ {
+ array.add_object_element(write(item));
+ }
+ }, (object, array) => {
+ unowned GLib.List<Object> list = (GLib.List<Object>)object;
+ array.foreach_element((a, index, node) => {
+ list.append(read(node.get_object()));
+ });
+ });
+
+ // Gee.List
+ register(typeof(Gee.List), (object, array) => {
+ foreach (var item in object as Gee.List<GLib.Object>)
+ {
+ array.add_object_element(write(item));
+ }
+ }, (object, array) => {
+ var list = object as Gee.List<GLib.Object>;
+ array.foreach_element((a, index, node) => {
+ list.insert(list.size, read(node.get_object()));
+ });
+ });
+ }
+
+ private static CollectionIterator find_iterator(Type type)
+ {
+ if (collections.has_key(type))
{
- critical("%s", Seed.Exception.to_string(context, e));
+ // vala bug
+ var box = collections.get(type);
+ return box.iterator;
}
- unowned Seed.Value val = Seed.evaluate(context, script, null);
+ foreach (var t in type.interfaces())
+ {
+ if (collections.has_key(t))
+ {
+ // vala bug again
+ var box = collections.get(t);
+ return box.iterator;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Allows collections of arbitrary types to be serialized.
+ *
+ * @param object The collection object to be serialized into a JSON array.
+ * @param array The JSON array to add elements to.
+ */
+ public delegate void CollectionIterator(GLib.Object object,
+ Json.Array array);
+
+ /**
+ * Allows collections of arbitrary types to be deserialized.
+ *
+ * @param object The collection object to be deserialized into.
+ * @param array The JSON array to deserialize.
+ */
+ public delegate void CollectionBuilder(GLib.Object object,
+ Json.Array array);
+
+ /**
+ * Delegates cannot be used as generic type arguments in Vala, so we have to
+ * box them.
+ */
+ private class CollectionBox
+ {
+ public CollectionIterator iterator;
+ public CollectionBuilder builder;
- e = null;
- if ((e = script.exception()) != null)
+ public CollectionBox(CollectionIterator iter, CollectionBuilder builder)
{
- critical("%s", Seed.Exception.to_string(context, e));
+ this.iterator = iter;
+ this.builder = builder;
}
}
}
+
diff --git a/ease-core/ease-slide.vala b/ease-core/ease-slide.vala
index eb55230..a370914 100644
--- a/ease-core/ease-slide.vala
+++ b/ease-core/ease-slide.vala
@@ -22,7 +22,7 @@
* children. The currently selected Slide is often acted upon by an
* EditorWindow (from main Ease, not core).
*/
-public class Ease.Slide : GLib.Object, UndoSource
+public class Ease.Slide : GLib.Object, UndoSource, Serializable
{
public const string IMAGE_TYPE = "EaseImageElement";
public const string SHAPE_TYPE = "EaseShapeElement";
@@ -32,7 +32,11 @@ public class Ease.Slide : GLib.Object, UndoSource
/**
* The { link Element}s contained by this Slide
*/
- internal Gee.LinkedList<Element> elements = new Gee.LinkedList<Element>();
+ internal Gee.LinkedList<Element> elements
+ {
+ get; private set;
+ default = new Gee.LinkedList<Element>();
+ }
/**
* The Slide's transition
@@ -162,6 +166,15 @@ public class Ease.Slide : GLib.Object, UndoSource
}
/**
+ * The properties that should be excluded from serialization.
+ */
+ public string[] serialize_exclude()
+ {
+ return { "next", "previous", "count", "width", "height", "aspect",
+ "background_abs", "parent" };
+ }
+
+ /**
* Emitted when an { link Element} or property of this Slide is changed.
*/
public signal void changed(Slide self);
@@ -296,7 +309,6 @@ public class Ease.Slide : GLib.Object, UndoSource
break;
}
- e.element_type = type;
append(e);
}
}
diff --git a/ease/ease-editor-window.vala b/ease/ease-editor-window.vala
index fa35b49..2fa6807 100644
--- a/ease/ease-editor-window.vala
+++ b/ease/ease-editor-window.vala
@@ -502,7 +502,6 @@ internal class Ease.EditorWindow : Gtk.Window
e.x = slide.width / 2 - width / 2;
e.y = slide.height / 2 - height / 2;
- e.element_type = Slide.IMAGE_TYPE;
e.identifier = Theme.CUSTOM_MEDIA;
e.filename = document.add_media_file(filename);
e.source_filename = filename;
@@ -534,7 +533,6 @@ internal class Ease.EditorWindow : Gtk.Window
e.x = slide.width / 2 - e.width / 2;
e.y = slide.height / 2 - e.height / 2;
- e.element_type = Slide.VIDEO_TYPE;
e.identifier = Theme.CUSTOM_MEDIA;
e.filename = document.add_media_file(filename);
e.source_filename = filename;
@@ -587,7 +585,6 @@ internal class Ease.EditorWindow : Gtk.Window
e.x = slide.width / 2 - e.width / 2;
e.y = slide.height / 2 - e.height / 2;
- e.element_type = Slide.PDF_TYPE;
e.identifier = Theme.CUSTOM_MEDIA;
e.filename = document.add_media_file(filename);
e.source_filename = filename;
diff --git a/ease/ease-main.vala b/ease/ease-main.vala
index 39ef32e..ddfdeb8 100644
--- a/ease/ease-main.vala
+++ b/ease/ease-main.vala
@@ -61,6 +61,9 @@ internal class Ease.Main : GLib.Object
Environment.set_application_name("Ease");
Gtk.Window.set_default_icon_name("ease");
+ // use built in serializer iterators
+ Serializer.register_builtins();
+
// parse command line options
var context = new OptionContext(_(" - a presentation editor"));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]