parser: regenerate with lambda
[nit.git] / lib / msgpack / serialization_read.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Deserialize full Nit objects from MessagePack format
16 #
17 # See the package `msgpack` for more details on the serialization
18 # of Nit objects.
19 module serialization_read
20
21 import serialization::caching
22 import serialization::safe
23 private import json # for class_inheritance_metamodel
24 private import serialization::engine_tools
25
26 import serialization_common
27 private import read
28 import ext
29
30 # ---
31 # Easy services
32
33 redef class Bytes
34
35 # Deserialize full Nit `nullable Object` from MessagePack formated data
36 #
37 # The dynamic type of the deserialized object can be limited to `static_type`.
38 #
39 # Warning: Deserialization errors are reported with `print_error`,
40 # the returned object may be partial or fall back on `null`.
41 # To handle the errors programmatically, use a `MsgPackDeserializer`.
42 fun deserialize_msgpack(static_type: nullable String): nullable Object
43 do
44 var stream = new BytesReader(self)
45 var res = stream.deserialize_msgpack(static_type)
46 stream.close
47 return res
48 end
49 end
50
51 redef class Reader
52
53 # Deserialize full Nit `nullable Object` from MessagePack formated data
54 #
55 # This method use metadata in the MessagePack source to recreate full
56 # Nit objects serialized by `Writer::serialize_msgpack` or
57 # `MsgPackSerializer`.
58 #
59 # The dynamic type of the deserialized object can be limited to `static_type`.
60 #
61 # Warning: Deserialization errors are reported with `print_error`,
62 # the returned object may be partial or fall back on `null`.
63 # To handle the errors programmatically, use a `MsgPackDeserializer`.
64 fun deserialize_msgpack(static_type: nullable String): nullable Object
65 do
66 var deserializer = new MsgPackDeserializer(self)
67 var res = deserializer.deserialize(static_type)
68
69 if deserializer.errors.length == 1 then
70 print_error deserializer.errors.join("")
71 else if deserializer.errors.not_empty then
72 print_error "Deserialization Errors:\n* {deserializer.errors.join("\n* ")}"
73 end
74
75 return res
76 end
77 end
78
79 # ---
80 # Engine
81
82 # Deserialize MessagePack format to full Nit objects
83 class MsgPackDeserializer
84 super CachingDeserializer
85 super MsgPackEngine
86 super SafeDeserializer
87
88 # Source stream
89 var stream: Reader
90
91 # Map of attributes from the root deserialized object to the current object
92 private var path = new Array[Map[nullable Serializable, nullable Serializable]]
93
94 # Metadata arrays with from the root deserialized object to the current object
95 var path_arrays = new Array[nullable Array[nullable Object]]
96
97 # Names of the attributes from the root to the object currently being deserialized
98 var attributes_path = new Array[String]
99
100 # Last encountered object reference id.
101 #
102 # See `id_to_object`.
103 private var just_opened_id: nullable Int = null
104
105 redef fun deserialize_attribute(name, static_type)
106 do
107 if path.is_empty then
108 # The was a parsing error or the root is not an object
109 deserialize_attribute_missing = false
110 return null
111 end
112
113 var current = path.last
114
115 var serialized_value = null
116 var serialized_value_found = false
117 if current.keys.has(name) then
118 # Non-cached string
119 serialized_value = current[name]
120 serialized_value_found = true
121 else
122 # It may be cached, deserialize all keys until we find it
123 for key in current.keys.to_a do
124 if key isa Array[nullable Serializable] or key isa MsgPackExt then
125 var str = convert_object(key, "String")
126 if str isa String then
127 var value = current[key]
128 current.keys.remove key
129 current[str] = value
130
131 if str == name then
132 serialized_value = value
133 serialized_value_found = true
134 break
135 end
136 end
137 end
138 end
139 end
140
141 if not serialized_value_found then
142 # Let the generated code / caller of `deserialize_attribute` raise the missing attribute error
143 deserialize_attribute_missing = true
144 return null
145 end
146
147 attributes_path.add name
148 var res = convert_object(serialized_value, static_type)
149 attributes_path.pop
150
151 deserialize_attribute_missing = false
152 return res
153 end
154
155 # This may be called multiple times by the same object from defs of a same constructor
156 redef fun notify_of_creation(new_object)
157 do
158 var id = just_opened_id
159 if id == null then return
160 cache[id] = new_object
161 end
162
163 # Convert the simple JSON `object` to a Nit object
164 private fun convert_object(object: nullable Object, static_type: nullable String): nullable Object
165 do
166 #print "convert_object {if object != null then object.class_name else "null"}"
167 if object isa Array[nullable Object] and object.length >= 1 then
168 # Serialized object?
169 var first = object.first
170 if first isa MsgPackExt then
171 if first.typ == ext_typ_obj then
172 # An array starts with a *ext*, it must be a serialized object
173
174 # New object declaration
175 var id = first.data.to_i
176
177 if cache.has_id(id) then
178 # FIXME use Warning
179 errors.add new Error("Deserialization Error: object with id {id} is deserialized twice.")
180 # Keep going
181 end
182
183 var type_name = null
184 var i = 1
185
186 # Read dynamic type
187 if object.length >= 2 then
188
189 # Try to get the type name as a string
190 var o = object[i]
191 if o isa String and static_type == "String" and object.length == 2 then
192 cache[id] = o
193 return o
194 else
195 var typ = convert_object(object[i], "String")
196 if typ isa String then
197 type_name = typ
198 i += 1
199 end
200 end
201 end
202
203 if type_name == null then
204 # There was no dynamic type
205
206 # We could use a `class_name_heuristic` here...
207
208 # Fallback to the static type
209 if static_type != null then
210 type_name = static_type.strip_nullable
211 end
212
213 if type_name == null then
214 errors.add new Error("Deserialization Error: could not determine dynamic type of `{object}`.")
215 return null
216 end
217 end
218
219 if not accept(type_name, static_type) then return null
220
221 var attributes = null
222 if object.length > i then attributes = object[i]
223 if not attributes isa Map[nullable Serializable, nullable Serializable] then
224 # Some other type (could be an error), or there's no attributes
225 attributes = new Map[nullable Serializable, nullable Serializable]
226 end
227
228 # advance on path
229 path.push attributes
230 path_arrays.push object
231
232 just_opened_id = id
233 var value = deserialize_class(type_name)
234 just_opened_id = null
235
236 # revert on path
237 path.pop
238 path_arrays.pop
239
240 return value
241 else
242 errors.add new Error("Deserialization Error: unknown MessagePack ext '{first.typ}'.")
243 end
244 end
245
246 # Plain array? Try to convert it to the desired static_type
247 if static_type != null then
248 return deserialize_class(static_type.strip_nullable)
249 end
250 return object
251 end
252
253 if object isa Map[nullable Serializable, nullable Serializable] then
254 # Plain map
255 # TODO parse it as an instance of `static_type`
256
257 if static_type != null then
258 path.push object
259 path_arrays.push null
260
261 just_opened_id = null
262 var value = deserialize_class(static_type.strip_nullable)
263
264 path.pop
265 path_arrays.pop
266
267 return value
268 end
269
270 return object
271 end
272
273 if object isa MsgPackExt then
274
275 # First try the custom extensions
276 var custom = deserialize_ext(object, static_type)
277 if custom == null then
278
279 # No custom, go for deser standard references
280 if object.typ == ext_typ_ref then
281 # Reference to an object
282 var id = object.data.to_i
283 if not cache.has_id(id) then
284 errors.add new Error("Deserialization Error: object reference id unknown.")
285 return object
286 end
287 return cache.object_for(id)
288
289 else if object.typ == ext_typ_char then
290 # Char
291 return object.data.to_s.first
292
293 else if object.typ == ext_typ_byte then
294 # Byte
295 return object.data.first
296 end
297 end
298 end
299
300 if object isa String and object.length == 1 and static_type == "Char" then
301 # Char serialized as a string
302 return object.chars.first
303 end
304
305 if object isa Int and static_type == "Byte" then
306 # Byte serialized as an integer
307 return object.to_b
308 end
309
310 return object
311 end
312
313 redef fun deserialize(static_type)
314 do
315 errors.clear
316
317 var root = stream.read_msgpack
318 return convert_object(root, static_type)
319 end
320
321 # Hook to customize the deserialization of MessagePack extensions
322 #
323 # Redefine this method in subclasses to return custom Nit objects from
324 # an application specific extension.
325 #
326 # This method is invoked before dealing with the extensions used by the
327 # Nit serialization metadata [0x40..0x43]. In general, you should ignore
328 # them by returning `null`, but they can also be intercepted to comply to
329 # a format from a remote server.
330 protected fun deserialize_ext(ext: MsgPackExt, static_type: nullable String): nullable Object
331 do
332 return null
333 end
334 end
335
336 redef class SimpleCollection[E]
337 redef init from_deserializer(v)
338 do
339 super
340 if v isa MsgPackDeserializer then
341 v.notify_of_creation self
342 init
343
344 var open_array = v.path_arrays.last
345 var msgpack_items = null
346 if open_array != null then msgpack_items = open_array.last
347
348 if not msgpack_items isa Array[nullable Serializable] then
349 v.errors.add new Error("Deserialization Error: no items in source of `{class_name}`")
350 return
351 end
352
353 # Name of the dynamic name of E
354 var items_type_name = (new GetName[E]).to_s
355
356 # Fill array
357 for o in msgpack_items do
358 var obj = v.convert_object(o, items_type_name)
359 if obj isa E then
360 add obj
361 else v.errors.add new AttributeTypeError(self, "items", obj, items_type_name)
362 end
363 end
364 end
365 end
366
367 redef class Map[K, V]
368 redef init from_deserializer(v)
369 do
370 super
371
372 if v isa MsgPackDeserializer then
373 v.notify_of_creation self
374 init
375
376 var open_object = v.path_arrays.last
377 var msgpack_items
378 if open_object != null then
379 # Metadata available
380 msgpack_items = open_object.last
381 else
382 msgpack_items = v.path.last
383 end
384
385 if not msgpack_items isa Map[nullable Object, nullable Object] then
386 v.errors.add new Error("Deserialization Error: no key/values in source of `{class_name}`")
387 return
388 end
389
390 var keys_type_name = (new GetName[K]).to_s
391 var values_type_name = (new GetName[V]).to_s
392
393 for key_src, value_src in msgpack_items do
394 var key = v.convert_object(key_src, keys_type_name)
395 if not key isa K then
396 v.errors.add new AttributeTypeError(self, "keys", key, keys_type_name)
397 continue
398 end
399
400 var value = v.convert_object(value_src, values_type_name)
401 if not value isa V then
402 v.errors.add new AttributeTypeError(self, "values", value, values_type_name)
403 continue
404 end
405
406 self[key] = value
407 end
408 end
409 end
410 end