X-Git-Url: http://nitlanguage.org diff --git a/lib/serialization/serialization.nit b/lib/serialization/serialization.nit index 3dabc36..597ae98 100644 --- a/lib/serialization/serialization.nit +++ b/lib/serialization/serialization.nit @@ -16,7 +16,7 @@ # Abstract services to serialize Nit objects to different formats # -# This module declares the `auto_serializable` annotation to mark Nit classes as serializable. +# This module declares the `serialize` annotation to mark Nit classes as serializable. # For an introduction to this service, refer to the documentation of the `serialization` group. # This documentation provides more technical information on interesting entitie of this module. # @@ -44,8 +44,14 @@ # `notify_of_creation` must be redefined. module serialization is new_annotation auto_serializable + new_annotation serialize + new_annotation noserialize + new_annotation serialize_as end +intrude import core::queue +import meta + # Abstract serialization service to be sub-classed by specialized services. interface Serializer # Entry point method of this service, serialize the `object` @@ -54,6 +60,11 @@ interface Serializer # use double dispatch to customize the bahavior per serializable objects. fun serialize(object: nullable Serializable) is abstract + # The object currently serialized by `serialized` + # + # Can be used by a custom serializer to add domain-specific serialization behavior. + protected fun current_object: nullable Object is abstract + # Serialize an object, with full serialization or a simple reference protected fun serialize_reference(object: Serializable) is abstract @@ -78,6 +89,15 @@ interface Serializer return true end + # The method is called when a standard `value` is serialized + # + # The default behavior is to call `value.core_serialize_to(self)` but it + # can be redefined by a custom serializer to add domain-specific serialization behavior. + fun serialize_core(value: Serializable) + do + value.core_serialize_to(self) + end + # Warn of problems and potential errors (such as if an attribute # is not serializable) fun warn(msg: String) do print "Serialization warning: {msg}" @@ -85,28 +105,113 @@ end # Abstract deserialization service # -# After initialization of one of its sub-classes, call `deserialize` -interface Deserializer - # Main method of this class, returns a Nit object - fun deserialize: nullable Object is abstract +# The main service is `deserialize`. +abstract class Deserializer + # Deserialize and return an object, storing errors in the attribute `errors` + # + # If a `static_type` is given, only subtypes of the `static_type` are accepted. + # + # This method behavior varies according to the implementation engines. + fun deserialize(static_type: nullable String): nullable Object is abstract + + # Deserialize the attribute with `name` from the object open for deserialization + # + # The `static_type` restricts what kind of object can be deserialized. + # + # Return the deserialized value or null on error, and set + # `deserialize_attribute_missing` to whether the attribute was missing. + # + # Internal method to be implemented by the engines. + fun deserialize_attribute(name: String, static_type: nullable String): nullable Object is abstract - # Internal method to be implemented by sub-classes - fun deserialize_attribute(name: String): nullable Object is abstract + # Was the attribute queried by the last call to `deserialize_attribute` missing? + var deserialize_attribute_missing = false - # Internal method called by objects in creation, - # to be implemented by sub-classes + # Register a newly allocated object (even if not completely built) + # + # Internal method called by objects in creation, to be implemented by the engines. fun notify_of_creation(new_object: Object) is abstract # Deserialize the next available object as an instance of `class_name` # - # Returns the deserialized object on success, aborts on error. + # Return the deserialized object on success and + # record in `errors` if `class_name` is unknown. # # This method should be redefined for each custom subclass of `Serializable`. # All refinement should look for a precise `class_name` and call super # on unsupported classes. - fun deserialize_class(class_name: String): Object do - print "Error: doesn't know how to deserialize class \"{class_name}\"" - abort + protected fun deserialize_class(class_name: String): nullable Object do + if class_name == "Error" then return new Error.from_deserializer(self) + return deserialize_class_intern(class_name) + end + + # Generated service to deserialize the next available object as an instance of `class_name` + # + # Refinements to this method will be generated by the serialization phase. + # To avoid conflicts, there should not be any other refinements to this method. + # You can instead use `deserialize_class`. + protected fun deserialize_class_intern(class_name: String): nullable Object do + errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"") + return null + end + + # Should `self` keep trying to deserialize an object after an error? + # + # This behavior takes effect after each attribute deserialization with + # errors such as a missing attribute or the value is of the wrong type. + # If `keep_going`, the attribute will be skipped but the engine will + # deserialize the next attribute. + # If `not keep_going`, the engine stops deserializing right away. + # + # When at `true`, this may cause the accumulation of a lot of entries in `errors`. + # + # Default at `true`. + var keep_going: nullable Bool = null is writable + + # Errors encountered in the last call to `deserialize` + var errors = new Array[Error] +end + +# Deserialization error related to an attribute of `receiver` +abstract class AttributeError + super Error + + # Parent object of the problematic attribute + var receiver: Object + + # Name of the problematic attribute in `receiver` + var attribute_name: String +end + +# Invalid dynamic type for a deserialized attribute +class AttributeTypeError + super AttributeError + + autoinit receiver, attribute_name, attribute, expected_type + + # Deserialized object that isn't of the `expected_type` + var attribute: nullable Object + + # Name of the type expected for `attribute` + var expected_type: String + + redef var message is lazy do + var attribute = attribute + var found_type = if attribute != null then attribute.class_name else "null" + + return "Deserialization Error: { + }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`" + end +end + +# Missing attribute at deserialization +class AttributeMissingError + super AttributeError + + autoinit receiver, attribute_name + + redef var message is lazy do + return "Deserialization Error: Missing attribute `{receiver.class_name}::{attribute_name}`" end end @@ -138,7 +243,7 @@ interface Serializable # Create an instance of this class from the `deserializer` # # This constructor is refined by subclasses to correctly build their instances. - init from_deserializer(deserializer: Deserializer) do end + init from_deserializer(deserializer: Deserializer) is nosuper do end end redef interface Object @@ -166,7 +271,131 @@ redef class Bool super DirectSerializable end redef class Char super DirectSerializable end redef class Int super DirectSerializable end redef class Float super DirectSerializable end -redef class NativeString super DirectSerializable end -redef class String super DirectSerializable end +redef class CString super DirectSerializable end +redef class Text super DirectSerializable end redef class SimpleCollection[E] super Serializable end redef class Map[K, V] super Serializable end + +redef class Couple[F, S] + super Serializable + + redef init from_deserializer(v) + do + v.notify_of_creation self + var first = v.deserialize_attribute("first") + var second = v.deserialize_attribute("second") + init(first, second) + end + + redef fun core_serialize_to(v) + do + v.serialize_attribute("first", first) + v.serialize_attribute("second", second) + end +end + +redef class Ref[E] + super Serializable + + redef init from_deserializer(v) + do + v.notify_of_creation self + var item = v.deserialize_attribute("item") + init item + end + + redef fun core_serialize_to(v) + do + v.serialize_attribute("item", first) + end +end + +redef class Error + super Serializable + + redef init from_deserializer(v) + do + v.notify_of_creation self + + var message = v.deserialize_attribute("message") + if not message isa String then message = "" + init message + + var cause = v.deserialize_attribute("cause") + if cause isa nullable Error then self.cause = cause + end + + redef fun core_serialize_to(v) + do + v.serialize_attribute("message", message) + v.serialize_attribute("cause", cause) + end +end + +# --- +# core::queue classes + +redef abstract class ProxyQueue[E] + + redef init from_deserializer(v) + do + v.notify_of_creation self + + var seq = v.deserialize_attribute("seq", (new GetName[Sequence[E]]).to_s) + if not seq isa Sequence[E] then seq = new Array[E] + if v.deserialize_attribute_missing then + v.errors.add new AttributeMissingError(self, "seq") + end + + init seq + end + + redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq) +end + +redef class RandQueue[E] + + redef init from_deserializer(v) + do + v.notify_of_creation self + + var seq = v.deserialize_attribute("seq", (new GetName[SimpleCollection[E]]).to_s) + if not seq isa SimpleCollection[E] then seq = new Array[E] + if v.deserialize_attribute_missing then + v.errors.add new AttributeMissingError(self, "seq") + end + + init seq + end + + redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq) +end + +redef class MinHeap[E] + + redef init from_deserializer(v) + do + v.notify_of_creation self + + var items = v.deserialize_attribute("items", (new GetName[SimpleCollection[E]]).to_s) + if not items isa Array[E] then items = new Array[E] + if v.deserialize_attribute_missing then + v.errors.add new AttributeMissingError(self, "items") + end + + var comparator = v.deserialize_attribute("comparator", "Comparator") + if not comparator isa Comparator then comparator = default_comparator + if v.deserialize_attribute_missing then + v.errors.add new AttributeMissingError(self, "comparator") + end + + init comparator + self.items.add_all items + end + + redef fun core_serialize_to(v) + do + v.serialize_attribute("items", items) + v.serialize_attribute("comparator", comparator) + end +end