X-Git-Url: http://nitlanguage.org diff --git a/lib/serialization/serialization.nit b/lib/serialization/serialization.nit index 8a1d1ee..7c689b6 100644 --- a/lib/serialization/serialization.nit +++ b/lib/serialization/serialization.nit @@ -14,24 +14,58 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Offers services to serialize a Nit objects to different persistent formats +# Abstract services to serialize Nit objects to different formats +# +# 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. +# +# Interesting entities for end users of serializable classes: +# +# * Serialize an instance subclass of `Serializable` with either +# `Serializer::serializable` and `Serializable::serialize`. +# * Deserialize an object using `Deserializer::deserialize`. +# The object type must the be checked with an `assert` or otherwise. +# +# Interesting entities to create custom serializable classes: +# +# * Subclass `Serializable` to declare a class as serializable and to customize +# the serialization and deserialization behavior. +# * Redefine `Serializable::core_serialize_to` to customize the serialization +# of the receiver class. +# * Redefine `Deserializer::deserialize_class` to customize the deserialization +# of a specific class by name. +# +# Interesting entities for serialization format: +# +# * Subclass `Serializer` and `Deserializer` with custom serices. +# * In `Serializer`, `serialize` and `serialize_reference` must be redefined. +# * In `Deserializer`; `deserialize`, `deserialize_attribute and +# `notify_of_creation` must be redefined. module serialization is new_annotation auto_serializable + new_annotation serialize + new_annotation noserialize end # Abstract serialization service to be sub-classed by specialized services. interface Serializer - # Main method of this service, serialize the `object` + # Entry point method of this service, serialize the `object` + # + # This method, and refinements, should handle `null` and probably + # use double dispatch to customize the bahavior per serializable objects. fun serialize(object: nullable Serializable) is abstract - # Serialize an object as a "possible" reference, depending of the service - fun serialize_reference(object: Serializable) is abstract + # Serialize an object, with full serialization or a simple reference + protected fun serialize_reference(object: Serializable) is abstract - # Serialize an attribute, used by `Serializable::core_serialize_to` + # Serialize an attribute to compose a serializable object + # + # This method should be called from `Serializable::core_serialize_to`. fun serialize_attribute(name: String, value: nullable Object) do if not try_to_serialize(value) then - warn("argument {value.class_name}::{name} is not serializable.") + warn("argument {name} of type {value.class_name} is not serializable.") end end @@ -54,7 +88,7 @@ end # Abstract deserialization service # # After initialization of one of its sub-classes, call `deserialize` -interface Deserializer +abstract class Deserializer # Main method of this class, returns a Nit object fun deserialize: nullable Object is abstract @@ -65,25 +99,112 @@ interface Deserializer # to be implemented by sub-classes fun notify_of_creation(new_object: Object) is abstract - # Mainly generated method to return the next instance of the givent - # class by name - fun deserialize_class(class_name: String): Object do - print "Error: doesn't know how to deserialize class \"{class_name}\"" - abort + # Deserialize the next available object as an instance of `class_name` + # + # Returns the deserialized object on success, aborts on error. + # + # 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. + protected fun deserialize_class(class_name: String): nullable Object do + 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 got wrong attribute names +class AttributeTypeError + super Error + + # Parent object of the problematic attribute + var receiver: Object + + # Name of the problematic attribute in `receiver` + var attribute_name: String + + # 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 fun to_s + 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 # Instances of this class can be passed to `Serializer::serialize` interface Serializable - # Full or true serialization - fun serialize_to(v: Serializer) do v.serialize(self) + # Serialize `self` to `serializer` + # + # This is a shortcut to `Serializer::serialize`. + fun serialize_to(serializer: Serializer) do serializer.serialize(self) + + # Actual serialization of `self` to `serializer` + # + # This writes the full data of `self` to `serializer`. + # + # This method can be redefined in sub classes and refinements. + # It should use `Serializer::serialize_attribute` to to register real or + # logical attributes. + # + # Any refinement should have its equivalent refinement of + # `Deserializer::deserialize_class` to support this custom deserialization. + fun core_serialize_to(serializer: Serializer) do end - # Body of the serialization of this class - # Can be redefed in sub classes and refinements - fun core_serialize_to(v: Serializer) do end + # Accept references or force direct serialization (using `serialize_to`) + # + # The subclass change the default behavior, which will accept references, + # to force to always serialize copies of `self`. + private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self) - # Whether full serialization (calls `serialize_to`) or place only references - fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self) + # 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) is nosuper do end +end + +redef interface Object + # Is `self` the same as `other` in a serialization context? + # + # Used to determine if an object has already been serialized. + fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other) + + # Hash value use for serialization + # + # Used in combination with `is_same_serialized`. If two objects are the same + # in a serialization context, they must have the same `serialization_hash`. + fun serialization_hash: Int do return object_id end # Instances of this class are not delayed and instead serialized immediately @@ -100,4 +221,39 @@ 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 Array[E] super Serializable 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