fa97009d86092b1ef779cb7c6c7c98fefce5c43b
[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 new_annotation serialize_as
50 end
51
52 intrude import core::queue
53 import meta
54
55 # Abstract serialization service to be sub-classed by specialized services.
56 interface Serializer
57 # Entry point method of this service, serialize the `object`
58 #
59 # This method, and refinements, should handle `null` and probably
60 # use double dispatch to customize the bahavior per serializable objects.
61 fun serialize(object: nullable Serializable) is abstract
62
63 # The object currently serialized by `serialized`
64 #
65 # Can be used by a custom serializer to add domain-specific serialization behavior.
66 protected fun current_object: nullable Object is abstract
67
68 # Serialize an object, with full serialization or a simple reference
69 protected fun serialize_reference(object: Serializable) is abstract
70
71 # Serialize an attribute to compose a serializable object
72 #
73 # This method should be called from `Serializable::core_serialize_to`.
74 fun serialize_attribute(name: String, value: nullable Object)
75 do
76 if not try_to_serialize(value) then
77 assert value != null # null would have been serialized
78 warn("argument {name} of type {value.class_name} is not serializable.")
79 end
80 end
81
82 # Serialize `value` is possible, i.e. it is `Serializable` or `null`
83 fun try_to_serialize(value: nullable Object): Bool
84 do
85 if value isa Serializable then
86 value.serialize_to_or_delay(self)
87 else if value == null then
88 serialize value
89 else return false
90 return true
91 end
92
93 # The method is called when a standard `value` is serialized
94 #
95 # The default behavior is to call `value.core_serialize_to(self)` but it
96 # can be redefined by a custom serializer to add domain-specific serialization behavior.
97 fun serialize_core(value: Serializable)
98 do
99 value.core_serialize_to(self)
100 end
101
102 # Warn of problems and potential errors (such as if an attribute
103 # is not serializable)
104 fun warn(msg: String) do print "Serialization warning: {msg}"
105 end
106
107 # Abstract deserialization service
108 #
109 # The main service is `deserialize`.
110 abstract class Deserializer
111 # Deserialize and return an object, storing errors in the attribute `errors`
112 #
113 # If a `static_type` is given, only subtypes of the `static_type` are accepted.
114 #
115 # This method behavior varies according to the implementation engines.
116 fun deserialize(static_type: nullable String): nullable Object is abstract
117
118 # Deserialize the attribute with `name` from the object open for deserialization
119 #
120 # The `static_type` restricts what kind of object can be deserialized.
121 #
122 # Return the deserialized value or null on error, and set
123 # `deserialize_attribute_missing` to whether the attribute was missing.
124 #
125 # Internal method to be implemented by the engines.
126 fun deserialize_attribute(name: String, static_type: nullable String): nullable Object is abstract
127
128 # Was the attribute queried by the last call to `deserialize_attribute` missing?
129 var deserialize_attribute_missing = false
130
131 # Register a newly allocated object (even if not completely built)
132 #
133 # Internal method called by objects in creation, to be implemented by the engines.
134 fun notify_of_creation(new_object: Object) is abstract
135
136 # Deserialize the next available object as an instance of `class_name`
137 #
138 # Return the deserialized object on success and
139 # record in `errors` if `class_name` is unknown.
140 #
141 # This method should be redefined for each custom subclass of `Serializable`.
142 # All refinement should look for a precise `class_name` and call super
143 # on unsupported classes.
144 protected fun deserialize_class(class_name: String): nullable Object do
145 if class_name == "Error" then return new Error.from_deserializer(self)
146 return deserialize_class_intern(class_name)
147 end
148
149 # Generated service to deserialize the next available object as an instance of `class_name`
150 #
151 # Refinements to this method will be generated by the serialization phase.
152 # To avoid conflicts, there should not be any other refinements to this method.
153 # You can instead use `deserialize_class`.
154 protected fun deserialize_class_intern(class_name: String): nullable Object do
155 errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
156 return null
157 end
158
159 # Should `self` keep trying to deserialize an object after an error?
160 #
161 # This behavior takes effect after each attribute deserialization with
162 # errors such as a missing attribute or the value is of the wrong type.
163 # If `keep_going`, the attribute will be skipped but the engine will
164 # deserialize the next attribute.
165 # If `not keep_going`, the engine stops deserializing right away.
166 #
167 # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
168 #
169 # Default at `true`.
170 var keep_going: nullable Bool = null is writable
171
172 # Errors encountered in the last call to `deserialize`
173 var errors = new Array[Error]
174 end
175
176 # Deserialization error related to an attribute of `receiver`
177 abstract class AttributeError
178 super Error
179
180 # Parent object of the problematic attribute
181 var receiver: Object
182
183 # Name of the problematic attribute in `receiver`
184 var attribute_name: String
185 end
186
187 # Invalid dynamic type for a deserialized attribute
188 class AttributeTypeError
189 super AttributeError
190
191 autoinit receiver, attribute_name, attribute, expected_type
192
193 # Deserialized object that isn't of the `expected_type`
194 var attribute: nullable Object
195
196 # Name of the type expected for `attribute`
197 var expected_type: String
198
199 redef var message is lazy do
200 var attribute = attribute
201 var found_type = if attribute != null then attribute.class_name else "null"
202
203 return "Deserialization Error: {
204 }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
205 end
206 end
207
208 # Missing attribute at deserialization
209 class AttributeMissingError
210 super AttributeError
211
212 autoinit receiver, attribute_name
213
214 redef var message is lazy do
215 return "Deserialization Error: Missing attribute `{receiver.class_name}::{attribute_name}`"
216 end
217 end
218
219 # Instances of this class can be passed to `Serializer::serialize`
220 interface Serializable
221 # Serialize `self` to `serializer`
222 #
223 # This is a shortcut to `Serializer::serialize`.
224 fun serialize_to(serializer: Serializer) do serializer.serialize(self)
225
226 # Actual serialization of `self` to `serializer`
227 #
228 # This writes the full data of `self` to `serializer`.
229 #
230 # This method can be redefined in sub classes and refinements.
231 # It should use `Serializer::serialize_attribute` to to register real or
232 # logical attributes.
233 #
234 # Any refinement should have its equivalent refinement of
235 # `Deserializer::deserialize_class` to support this custom deserialization.
236 fun core_serialize_to(serializer: Serializer) do end
237
238 # Accept references or force direct serialization (using `serialize_to`)
239 #
240 # The subclass change the default behavior, which will accept references,
241 # to force to always serialize copies of `self`.
242 private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
243
244 # Create an instance of this class from the `deserializer`
245 #
246 # This constructor is refined by subclasses to correctly build their instances.
247 init from_deserializer(deserializer: Deserializer) is nosuper do end
248 end
249
250 redef interface Object
251 # Is `self` the same as `other` in a serialization context?
252 #
253 # Used to determine if an object has already been serialized.
254 fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
255
256 # Hash value use for serialization
257 #
258 # Used in combination with `is_same_serialized`. If two objects are the same
259 # in a serialization context, they must have the same `serialization_hash`.
260 fun serialization_hash: Int do return object_id
261 end
262
263 # Instances of this class are not delayed and instead serialized immediately
264 # This applies mainly to `universal` types
265 interface DirectSerializable
266 super Serializable
267
268 redef fun serialize_to_or_delay(v) do serialize_to(v)
269 end
270
271 redef class Bool super DirectSerializable end
272 redef class Char super DirectSerializable end
273 redef class Byte super DirectSerializable end
274 redef class Int super DirectSerializable end
275 redef class Float super DirectSerializable end
276 redef class CString super DirectSerializable end
277 redef class Text super DirectSerializable end
278 redef class SimpleCollection[E] super Serializable end
279 redef class Map[K, V] super Serializable end
280
281 redef class Couple[F, S]
282 super Serializable
283
284 redef init from_deserializer(v)
285 do
286 v.notify_of_creation self
287 var first = v.deserialize_attribute("first")
288 var second = v.deserialize_attribute("second")
289 init(first, second)
290 end
291
292 redef fun core_serialize_to(v)
293 do
294 v.serialize_attribute("first", first)
295 v.serialize_attribute("second", second)
296 end
297 end
298
299 redef class Ref[E]
300 super Serializable
301
302 redef init from_deserializer(v)
303 do
304 v.notify_of_creation self
305 var item = v.deserialize_attribute("item")
306 init item
307 end
308
309 redef fun core_serialize_to(v)
310 do
311 v.serialize_attribute("item", first)
312 end
313 end
314
315 redef class Error
316 super Serializable
317
318 redef init from_deserializer(v)
319 do
320 v.notify_of_creation self
321
322 var message = v.deserialize_attribute("message")
323 if not message isa String then message = ""
324 init message
325
326 var cause = v.deserialize_attribute("cause")
327 if cause isa nullable Error then self.cause = cause
328 end
329
330 redef fun core_serialize_to(v)
331 do
332 v.serialize_attribute("message", message)
333 v.serialize_attribute("cause", cause)
334 end
335 end
336
337 # ---
338 # core::queue classes
339
340 redef abstract class ProxyQueue[E]
341
342 redef init from_deserializer(v)
343 do
344 v.notify_of_creation self
345
346 var seq = v.deserialize_attribute("seq", (new GetName[Sequence[E]]).to_s)
347 if not seq isa Sequence[E] then seq = new Array[E]
348 if v.deserialize_attribute_missing then
349 v.errors.add new AttributeMissingError(self, "seq")
350 end
351
352 init seq
353 end
354
355 redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
356 end
357
358 redef class RandQueue[E]
359
360 redef init from_deserializer(v)
361 do
362 v.notify_of_creation self
363
364 var seq = v.deserialize_attribute("seq", (new GetName[SimpleCollection[E]]).to_s)
365 if not seq isa SimpleCollection[E] then seq = new Array[E]
366 if v.deserialize_attribute_missing then
367 v.errors.add new AttributeMissingError(self, "seq")
368 end
369
370 init seq
371 end
372
373 redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
374 end
375
376 redef class MinHeap[E]
377
378 redef init from_deserializer(v)
379 do
380 v.notify_of_creation self
381
382 var items = v.deserialize_attribute("items", (new GetName[SimpleCollection[E]]).to_s)
383 if not items isa Array[E] then items = new Array[E]
384 if v.deserialize_attribute_missing then
385 v.errors.add new AttributeMissingError(self, "items")
386 end
387
388 var comparator = v.deserialize_attribute("comparator", "Comparator")
389 if not comparator isa Comparator then comparator = default_comparator
390 if v.deserialize_attribute_missing then
391 v.errors.add new AttributeMissingError(self, "comparator")
392 end
393
394 init comparator
395 self.items.add_all items
396 end
397
398 redef fun core_serialize_to(v)
399 do
400 v.serialize_attribute("items", items)
401 v.serialize_attribute("comparator", comparator)
402 end
403 end