lib/serialization: fix deserialization of Error
[nit.git] / lib / serialization / serialization.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract services to serialize Nit objects to different formats
18 #
19 # This module declares the `serialize` annotation to mark Nit classes as serializable.
20 # For an introduction to this service, refer to the documentation of the `serialization` group.
21 # This documentation provides more technical information on interesting entitie of this module.
22 #
23 # Interesting entities for end users of serializable classes:
24 #
25 # * Serialize an instance subclass of `Serializable` with either
26 # `Serializer::serializable` and `Serializable::serialize`.
27 # * Deserialize an object using `Deserializer::deserialize`.
28 # The object type must the be checked with an `assert` or otherwise.
29 #
30 # Interesting entities to create custom serializable classes:
31 #
32 # * Subclass `Serializable` to declare a class as serializable and to customize
33 # the serialization and deserialization behavior.
34 # * Redefine `Serializable::core_serialize_to` to customize the serialization
35 # of the receiver class.
36 # * Redefine `Deserializer::deserialize_class` to customize the deserialization
37 # of a specific class by name.
38 #
39 # Interesting entities for serialization format:
40 #
41 # * Subclass `Serializer` and `Deserializer` with custom serices.
42 # * In `Serializer`, `serialize` and `serialize_reference` must be redefined.
43 # * In `Deserializer`; `deserialize`, `deserialize_attribute and
44 # `notify_of_creation` must be redefined.
45 module serialization is
46 new_annotation auto_serializable
47 new_annotation serialize
48 new_annotation noserialize
49 end
50
51 # Abstract serialization service to be sub-classed by specialized services.
52 interface Serializer
53 # Entry point method of this service, serialize the `object`
54 #
55 # This method, and refinements, should handle `null` and probably
56 # use double dispatch to customize the bahavior per serializable objects.
57 fun serialize(object: nullable Serializable) is abstract
58
59 # Serialize an object, with full serialization or a simple reference
60 protected fun serialize_reference(object: Serializable) is abstract
61
62 # Serialize an attribute to compose a serializable object
63 #
64 # This method should be called from `Serializable::core_serialize_to`.
65 fun serialize_attribute(name: String, value: nullable Object)
66 do
67 if not try_to_serialize(value) then
68 warn("argument {name} of type {value.class_name} is not serializable.")
69 end
70 end
71
72 # Serialize `value` is possie, i.e. it is `Serializable` or `null`
73 fun try_to_serialize(value: nullable Object): Bool
74 do
75 if value isa Serializable then
76 value.serialize_to_or_delay(self)
77 else if value == null then
78 serialize value
79 else return false
80 return true
81 end
82
83 # Warn of problems and potential errors (such as if an attribute
84 # is not serializable)
85 fun warn(msg: String) do print "Serialization warning: {msg}"
86 end
87
88 # Abstract deserialization service
89 #
90 # After initialization of one of its sub-classes, call `deserialize`
91 abstract class Deserializer
92 # Main method of this class, returns a Nit object
93 fun deserialize: nullable Object is abstract
94
95 # Internal method to be implemented by sub-classes
96 fun deserialize_attribute(name: String): nullable Object is abstract
97
98 # Internal method called by objects in creation,
99 # to be implemented by sub-classes
100 fun notify_of_creation(new_object: Object) is abstract
101
102 # Deserialize the next available object as an instance of `class_name`
103 #
104 # Returns the deserialized object on success, aborts on error.
105 #
106 # This method should be redefined for each custom subclass of `Serializable`.
107 # All refinement should look for a precise `class_name` and call super
108 # on unsupported classes.
109 protected fun deserialize_class(class_name: String): nullable Object do
110 if class_name == "Error" then return new Error.from_deserializer(self)
111 return deserialize_class_intern(class_name)
112 end
113
114 # Generated service to deserialize the next available object as an instance of `class_name`
115 #
116 # Refinements to this method will be generated by the serialization phase.
117 # To avoid conflicts, there should not be any other refinements to this method.
118 # You can instead use `deserialize_class`.
119 protected fun deserialize_class_intern(class_name: String): nullable Object do
120 errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
121 return null
122 end
123
124 # Should `self` keep trying to deserialize an object after an error?
125 #
126 # This behavior takes effect after each attribute deserialization with
127 # errors such as a missing attribute or the value is of the wrong type.
128 # If `keep_going`, the attribute will be skipped but the engine will
129 # deserialize the next attribute.
130 # If `not keep_going`, the engine stops deserializing right away.
131 #
132 # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
133 #
134 # Default at `true`.
135 var keep_going: nullable Bool = null is writable
136
137 # Errors encountered in the last call to `deserialize`
138 var errors = new Array[Error]
139 end
140
141 # Deserialization got wrong attribute names
142 class AttributeTypeError
143 super Error
144
145 # Parent object of the problematic attribute
146 var receiver: Object
147
148 # Name of the problematic attribute in `receiver`
149 var attribute_name: String
150
151 # Deserialized object that isn't of the `expected_type`
152 var attribute: nullable Object
153
154 # Name of the type expected for `attribute`
155 var expected_type: String
156
157 redef fun to_s
158 do
159 var attribute = attribute
160 var found_type = if attribute != null then attribute.class_name else "null"
161
162 return "Deserialization Error: {
163 }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
164 end
165 end
166
167 # Instances of this class can be passed to `Serializer::serialize`
168 interface Serializable
169 # Serialize `self` to `serializer`
170 #
171 # This is a shortcut to `Serializer::serialize`.
172 fun serialize_to(serializer: Serializer) do serializer.serialize(self)
173
174 # Actual serialization of `self` to `serializer`
175 #
176 # This writes the full data of `self` to `serializer`.
177 #
178 # This method can be redefined in sub classes and refinements.
179 # It should use `Serializer::serialize_attribute` to to register real or
180 # logical attributes.
181 #
182 # Any refinement should have its equivalent refinement of
183 # `Deserializer::deserialize_class` to support this custom deserialization.
184 fun core_serialize_to(serializer: Serializer) do end
185
186 # Accept references or force direct serialization (using `serialize_to`)
187 #
188 # The subclass change the default behavior, which will accept references,
189 # to force to always serialize copies of `self`.
190 private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
191
192 # Create an instance of this class from the `deserializer`
193 #
194 # This constructor is refined by subclasses to correctly build their instances.
195 init from_deserializer(deserializer: Deserializer) is nosuper do end
196 end
197
198 redef interface Object
199 # Is `self` the same as `other` in a serialization context?
200 #
201 # Used to determine if an object has already been serialized.
202 fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
203
204 # Hash value use for serialization
205 #
206 # Used in combination with `is_same_serialized`. If two objects are the same
207 # in a serialization context, they must have the same `serialization_hash`.
208 fun serialization_hash: Int do return object_id
209 end
210
211 # Instances of this class are not delayed and instead serialized immediately
212 # This applies mainly to `universal` types
213 interface DirectSerializable
214 super Serializable
215
216 redef fun serialize_to_or_delay(v) do serialize_to(v)
217 end
218
219 redef class Bool super DirectSerializable end
220 redef class Char super DirectSerializable end
221 redef class Int super DirectSerializable end
222 redef class Float super DirectSerializable end
223 redef class NativeString super DirectSerializable end
224 redef class String super DirectSerializable end
225 redef class SimpleCollection[E] super Serializable end
226 redef class Map[K, V] super Serializable end
227
228 redef class Couple[F, S]
229 super Serializable
230
231 redef init from_deserializer(v)
232 do
233 v.notify_of_creation self
234 var first = v.deserialize_attribute("first")
235 var second = v.deserialize_attribute("second")
236 init(first, second)
237 end
238
239 redef fun core_serialize_to(v)
240 do
241 v.serialize_attribute("first", first)
242 v.serialize_attribute("second", second)
243 end
244 end
245
246 redef class Ref[E]
247 super Serializable
248
249 redef init from_deserializer(v)
250 do
251 v.notify_of_creation self
252 var item = v.deserialize_attribute("item")
253 init item
254 end
255
256 redef fun core_serialize_to(v)
257 do
258 v.serialize_attribute("item", first)
259 end
260 end
261
262 redef class Error
263 super Serializable
264
265 redef init from_deserializer(v)
266 do
267 v.notify_of_creation self
268
269 var message = v.deserialize_attribute("message")
270 if not message isa String then message = ""
271 init message
272
273 var cause = v.deserialize_attribute("cause")
274 if cause isa nullable Error then self.cause = cause
275 end
276
277 redef fun core_serialize_to(v)
278 do
279 v.serialize_attribute("message", message)
280 v.serialize_attribute("cause", cause)
281 end
282 end