serialization: AttributeTypeError doesn't need the message attribute, fix TODO
[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 # Abstract serialization service to be sub-classed by specialized services.
53 interface Serializer
54 # Entry point method of this service, serialize the `object`
55 #
56 # This method, and refinements, should handle `null` and probably
57 # use double dispatch to customize the bahavior per serializable objects.
58 fun serialize(object: nullable Serializable) is abstract
59
60 # Serialize an object, with full serialization or a simple reference
61 protected fun serialize_reference(object: Serializable) is abstract
62
63 # Serialize an attribute to compose a serializable object
64 #
65 # This method should be called from `Serializable::core_serialize_to`.
66 fun serialize_attribute(name: String, value: nullable Object)
67 do
68 if not try_to_serialize(value) then
69 warn("argument {name} of type {value.class_name} is not serializable.")
70 end
71 end
72
73 # Serialize `value` is possie, i.e. it is `Serializable` or `null`
74 fun try_to_serialize(value: nullable Object): Bool
75 do
76 if value isa Serializable then
77 value.serialize_to_or_delay(self)
78 else if value == null then
79 serialize value
80 else return false
81 return true
82 end
83
84 # Warn of problems and potential errors (such as if an attribute
85 # is not serializable)
86 fun warn(msg: String) do print "Serialization warning: {msg}"
87 end
88
89 # Abstract deserialization service
90 #
91 # After initialization of one of its sub-classes, call `deserialize`
92 abstract class Deserializer
93 # Main method of this class, returns a Nit object
94 fun deserialize: nullable Object is abstract
95
96 # Internal method to be implemented by sub-classes
97 fun deserialize_attribute(name: String): nullable Object is abstract
98
99 # Internal method called by objects in creation,
100 # to be implemented by sub-classes
101 fun notify_of_creation(new_object: Object) is abstract
102
103 # Deserialize the next available object as an instance of `class_name`
104 #
105 # Returns the deserialized object on success, aborts on error.
106 #
107 # This method should be redefined for each custom subclass of `Serializable`.
108 # All refinement should look for a precise `class_name` and call super
109 # on unsupported classes.
110 protected fun deserialize_class(class_name: String): nullable Object do
111 if class_name == "Error" then return new Error.from_deserializer(self)
112 return deserialize_class_intern(class_name)
113 end
114
115 # Generated service to deserialize the next available object as an instance of `class_name`
116 #
117 # Refinements to this method will be generated by the serialization phase.
118 # To avoid conflicts, there should not be any other refinements to this method.
119 # You can instead use `deserialize_class`.
120 protected fun deserialize_class_intern(class_name: String): nullable Object do
121 errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
122 return null
123 end
124
125 # Should `self` keep trying to deserialize an object after an error?
126 #
127 # This behavior takes effect after each attribute deserialization with
128 # errors such as a missing attribute or the value is of the wrong type.
129 # If `keep_going`, the attribute will be skipped but the engine will
130 # deserialize the next attribute.
131 # If `not keep_going`, the engine stops deserializing right away.
132 #
133 # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
134 #
135 # Default at `true`.
136 var keep_going: nullable Bool = null is writable
137
138 # Errors encountered in the last call to `deserialize`
139 var errors = new Array[Error]
140 end
141
142 # Deserialization got wrong attribute names
143 class AttributeTypeError
144 super Error
145
146 autoinit receiver, attribute_name, attribute, expected_type
147
148 # Parent object of the problematic attribute
149 var receiver: Object
150
151 # Name of the problematic attribute in `receiver`
152 var attribute_name: String
153
154 # Deserialized object that isn't of the `expected_type`
155 var attribute: nullable Object
156
157 # Name of the type expected for `attribute`
158 var expected_type: String
159
160 redef var message is lazy do
161 var attribute = attribute
162 var found_type = if attribute != null then attribute.class_name else "null"
163
164 return "Deserialization Error: {
165 }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
166 end
167 end
168
169 # Instances of this class can be passed to `Serializer::serialize`
170 interface Serializable
171 # Serialize `self` to `serializer`
172 #
173 # This is a shortcut to `Serializer::serialize`.
174 fun serialize_to(serializer: Serializer) do serializer.serialize(self)
175
176 # Actual serialization of `self` to `serializer`
177 #
178 # This writes the full data of `self` to `serializer`.
179 #
180 # This method can be redefined in sub classes and refinements.
181 # It should use `Serializer::serialize_attribute` to to register real or
182 # logical attributes.
183 #
184 # Any refinement should have its equivalent refinement of
185 # `Deserializer::deserialize_class` to support this custom deserialization.
186 fun core_serialize_to(serializer: Serializer) do end
187
188 # Accept references or force direct serialization (using `serialize_to`)
189 #
190 # The subclass change the default behavior, which will accept references,
191 # to force to always serialize copies of `self`.
192 private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
193
194 # Create an instance of this class from the `deserializer`
195 #
196 # This constructor is refined by subclasses to correctly build their instances.
197 init from_deserializer(deserializer: Deserializer) is nosuper do end
198 end
199
200 redef interface Object
201 # Is `self` the same as `other` in a serialization context?
202 #
203 # Used to determine if an object has already been serialized.
204 fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
205
206 # Hash value use for serialization
207 #
208 # Used in combination with `is_same_serialized`. If two objects are the same
209 # in a serialization context, they must have the same `serialization_hash`.
210 fun serialization_hash: Int do return object_id
211 end
212
213 # Instances of this class are not delayed and instead serialized immediately
214 # This applies mainly to `universal` types
215 interface DirectSerializable
216 super Serializable
217
218 redef fun serialize_to_or_delay(v) do serialize_to(v)
219 end
220
221 redef class Bool super DirectSerializable end
222 redef class Char super DirectSerializable end
223 redef class Int super DirectSerializable end
224 redef class Float super DirectSerializable end
225 redef class NativeString super DirectSerializable end
226 redef class Text super DirectSerializable end
227 redef class SimpleCollection[E] super Serializable end
228 redef class Map[K, V] super Serializable end
229
230 redef class Couple[F, S]
231 super Serializable
232
233 redef init from_deserializer(v)
234 do
235 v.notify_of_creation self
236 var first = v.deserialize_attribute("first")
237 var second = v.deserialize_attribute("second")
238 init(first, second)
239 end
240
241 redef fun core_serialize_to(v)
242 do
243 v.serialize_attribute("first", first)
244 v.serialize_attribute("second", second)
245 end
246 end
247
248 redef class Ref[E]
249 super Serializable
250
251 redef init from_deserializer(v)
252 do
253 v.notify_of_creation self
254 var item = v.deserialize_attribute("item")
255 init item
256 end
257
258 redef fun core_serialize_to(v)
259 do
260 v.serialize_attribute("item", first)
261 end
262 end
263
264 redef class Error
265 super Serializable
266
267 redef init from_deserializer(v)
268 do
269 v.notify_of_creation self
270
271 var message = v.deserialize_attribute("message")
272 if not message isa String then message = ""
273 init message
274
275 var cause = v.deserialize_attribute("cause")
276 if cause isa nullable Error then self.cause = cause
277 end
278
279 redef fun core_serialize_to(v)
280 do
281 v.serialize_attribute("message", message)
282 v.serialize_attribute("cause", cause)
283 end
284 end