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`
# 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
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}"
# 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
- # Internal method to be implemented by sub-classes
- fun deserialize_attribute(name: 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 called by objects in creation,
- # to be implemented by sub-classes
+ # Was the attribute queried by the last call to `deserialize_attribute` missing?
+ var deserialize_attribute_missing = false
+
+ # 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
# 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
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
end
end
-redef class Container[E]
+redef class Ref[E]
super Serializable
redef init from_deserializer(v)
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