Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / neo.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 # Save and load a `Model` to/from a Neo4j graph.
16 #
17 # Nit models are composed by MEntities.
18 # This module creates NeoNode for each MEntity found in a `Model` and save them
19 # into Neo4j database.
20 #
21 # SEE: `Neo4jClient`
22 #
23 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
24 #
25 # Structure of the nit `Model` in the graph:
26 #
27 # Note : Any null or empty attribute will not be saved in the database.
28 #
29 # For any `MEntity` (in addition to specific data):
30 #
31 # * labels: model name (`model_name`) and `MEntity`.
32 # * `name`: short (unqualified) name.
33 # * `mdoc`: JSON array representing the associated Markdown documentation
34 # (one item by line).
35 #
36 # Note : All nodes described here are MEntities.
37 #
38 # `MPackage`
39 #
40 # * labels: `MPackage`, `model_name` and `MEntity`.
41 # * `(:MPackage)-[:ROOT]->(:MGroup)`: root of the group tree.
42 #
43 # `MGroup`
44 #
45 # * labels: `MGroup`, `model_name` and `MEntity`.
46 # * `(:MGroup)-[:PROJECT]->(:MPackage)`: associated package.
47 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
48 # group.
49 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
50 # this group.
51 # * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
52 # this group.
53 #
54 # `MModule`
55 #
56 # * labels: `MModule`, `model_name` and `MEntity`.
57 # * `location`: origin of the definition. SEE: `Location.to_s`
58 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
59 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
60 # module.
61 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
62 # this module.
63 #
64 # `MClass`
65 #
66 # * labels: `MClass`, `model_name` and `MEntity`.
67 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
68 # * `visibility`: visibility of the class.
69 # * `parameter_names`: JSON array listing the name of each formal generic
70 # parameter (in order of declaration).
71 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
72 #
73 # Arguments in the `CLASSTYPE` are named following the `parameter_names`
74 # attribute of the `MClassDef` that introduces the class. A class definition
75 # introduces a class if and only if it has this class as `MCLASS` and
76 # has `is_intro` set to `true`.
77 #
78 # `MClassDef`
79 #
80 # * labels: `MClassDef`, `model_name` and `MEntity`.
81 # * `location`: origin of the definition. SEE: `Location.to_s`
82 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
83 # classdef.
84 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
85 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
86 # the classdef.
87 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
88 # classdef (introductions and redefinitions).
89 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
90 #
91 # `MProperty`
92 #
93 # * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
94 # `MAttribute` `MVirtualTypeProp` or `MInnerClass`, depending on the class of
95 # the represented entity.
96 # * `visibility`: visibility of the property.
97 # * `is_init`: Indicates if the property is a constructor. Exists only if the
98 # node is a `MMethod`.
99 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
100 # the property.
101 #
102 # Additional relationship for `MInnerClass`:
103 #
104 # * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
105 #
106 # `MPropDef`
107 #
108 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
109 # `MAttributeDef`, `MVirtualTypeDef` or `MInnerClassDef`, depending on the
110 # class of the represented entity.
111 # * `location`: origin of the definition. SEE: `Location.to_s`.
112 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
113 #
114 # Additional attributes and relationship for `MMethodDef`:
115 #
116 # * `is_abstract`: Is the method definition abstract?
117 # * `is_intern`: Is the method definition intern?
118 # * `is_extern`: Is the method definition extern?
119 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
120 # property definition.
121 #
122 # Additional relationship for `MAttributeDef`:
123 #
124 # * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
125 # if specified.
126 #
127 # Additional relationship for `MVirtualTypeDef`:
128 #
129 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
130 # is bound in this definition. Exists only if this definition bound the virtual
131 # type to an effective type.
132 #
133 # Additional relationship for `MInnerClassDef`:
134 #
135 # * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
136 # definition.
137 #
138 # `MType`
139 #
140 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
141 # `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
142 # class of the represented entity.
143 #
144 # Additional label and relationships for `MClassType`:
145 #
146 # * If it is a `MGenericType`, also has the `MGenericType` label.
147 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
148 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
149 #
150 # Arguments are named following the `parameter_names` attribute of the
151 # `MClass` referred by `CLASS`.
152 #
153 # Additional relationship for `MVirtualType`:
154 #
155 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
156 # determines the type (usually a `MVirtualTypeProp`).
157 #
158 # Additional attribute and relationship for `MParameterType`:
159 #
160 # * `rank`: position of the parameter (0 for the first parameter).
161 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
162 # belong.
163 #
164 # Additional relationship for `MNullableType`:
165 #
166 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
167 #
168 # Additional attribute and relationship for `MRawType`:
169 #
170 # * `text`: JSON array of the parts’ text.
171 # * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
172 #
173 # Additional attribute and relationships for `MSignature`:
174 #
175 # * `parameter_names`: JSON array representing the list of the parameter names.
176 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
177 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
178 # procedures.
179 #
180 # In order to maintain the correct parameters order, each `MSignature` node
181 # contains an array of parameter names corresponding to the parameter order in
182 # the signature.
183 #
184 # For example, if the source code contains:
185 #
186 # ~~~nitish
187 # fun foo(a: A, b: B, c: C)
188 # ~~~
189 #
190 # The `MSignature` node will contain a property
191 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
192 # with the parameters in the correct order.
193 #
194 # `MParameter`
195 #
196 # * labels: `MParameter`, `model_name` and `MEntity`.
197 # * `is_vararg`: Is the parameter a vararg?
198 # * `rank`: position of the parameter (0 for the first parameter).
199 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
200 #
201 # MParameters are also ranked by their position in the corresponding signature.
202 # Rank 0 for the first parameter, 1 for the next one and etc.
203 #
204 # `MTypePart`
205 #
206 # * labels: `MTypePart`, `model_name` and `MEntity`.
207 # * `rank`: position in the `MRawType`.
208 # * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
209 module neo
210
211 import doc::model_ext
212 import neo4j
213 import toolcontext
214
215 # Helper class that can save and load a `Model` into a Neo4j database.
216 class NeoModel
217
218 # The model name.
219 #
220 # Because we use only one Neo4j instance to store all the models,
221 # we need to mark their appartenance to a particular model and avoid loading all models.
222 #
223 # The name is used as a Neo label on each created nodes and used to load nodes from base.
224 var model_name: String
225
226 # The toolcontext used to init the `NeoModel` tool.
227 var toolcontext: ToolContext
228
229 # The Neo4j `client` used to communicate with the Neo4j instance.
230 var client: Neo4jClient
231
232 # Fill `model` using base pointed by `client`.
233 fun load(model: Model): Model do
234 var nodes: Array[NeoNode]
235
236 toolcontext.info("Loading package node...", 1)
237 nodes = client.nodes_with_labels([model_name, "MPackage"])
238 for node in nodes do to_mpackage(model, node)
239 toolcontext.info("Loading groups...", 1)
240 nodes = client.nodes_with_labels([model_name, "MGroup"])
241 for node in nodes do to_mgroup(model, node)
242 toolcontext.info("Loading modules...", 1)
243 nodes = client.nodes_with_labels([model_name, "MModule"])
244 for node in nodes do to_mmodule(model, node)
245 toolcontext.info("Loading classes...", 1)
246 nodes = client.nodes_with_labels([model_name, "MClass"])
247 for node in nodes do to_mclass(model, node)
248 toolcontext.info("Loading class definitions...", 1)
249 nodes = client.nodes_with_labels([model_name, "MClassDef"])
250 for node in nodes do to_mclassdef(model, node)
251 toolcontext.info("Loading properties...", 1)
252 nodes = client.nodes_with_labels([model_name, "MProperty"])
253 for node in nodes do to_mproperty(model, node)
254 toolcontext.info("Loading property definitions...", 1)
255 nodes = client.nodes_with_labels([model_name, "MPropDef"])
256 for node in nodes do to_mpropdef(model, node)
257 return model
258 end
259
260 # Save `model` in the base pointed by `client`.
261 fun save(model: Model) do
262 var nodes = collect_model_nodes(model)
263 toolcontext.info("Save {nodes.length} nodes...", 1)
264 push_all(nodes)
265 var edges = collect_model_edges(model)
266 toolcontext.info("Save {edges.length} edges...", 1)
267 push_all(edges)
268 end
269
270 # Save `neo_entities` in base using batch mode.
271 private fun push_all(neo_entities: Collection[NeoEntity]) do
272 var batch = new NeoBatch(client)
273 var len = neo_entities.length
274 var sum = 0
275 var i = 1
276 for nentity in neo_entities do
277 batch.save_entity(nentity)
278 if i == batch_max_size then
279 do_batch(batch)
280 sum += batch_max_size
281 toolcontext.info(" {sum * 100 / len}% done", 1)
282 batch = new NeoBatch(client)
283 i = 1
284 else
285 i += 1
286 end
287 end
288 do_batch(batch)
289 end
290
291 # How many operation can be executed in one batch?
292 private var batch_max_size = 1000
293
294 # Execute `batch` and check for errors.
295 #
296 # Abort if `batch.execute` returns errors.
297 private fun do_batch(batch: NeoBatch) do
298 var errors = batch.execute
299 if not errors.is_empty then
300 print errors
301 exit(1)
302 end
303 end
304
305 # Collect all nodes from the current `model`.
306 private fun collect_model_nodes(model: Model): Collection[NeoNode] do
307 for mpackage in model.mpackages do
308 mpackage.to_node(nodes, model_name)
309 for mgroup in mpackage.mgroups do mgroup.to_node(nodes, model_name)
310 end
311 return nodes.values
312 end
313
314 # Collect all edges from the current `model`.
315 #
316 # Actually collect all out_edges from all nodes.
317 private fun collect_model_edges(model: Model): Collection[NeoEdge] do
318 var edges = new HashSet[NeoEdge]
319 for node in nodes.values do edges.add_all(node.out_edges)
320 return edges
321 end
322
323 # Mentities associated to nodes.
324 #
325 # The key is the node’s id.
326 private var mentities = new HashMap[Int, MEntity]
327
328 # Nodes associated with MEntities.
329 private var nodes = new HashMap[MEntity, NeoNode]
330
331 # Get the `MEntity` associated with `node`.
332 fun to_mentity(model: Model, node: NeoNode): MEntity do
333 if node.labels.has("MPackage") then return to_mpackage(model, node)
334 if node.labels.has("MGroup") then return to_mgroup(model, node)
335 if node.labels.has("MModule") then return to_mmodule(model, node)
336 if node.labels.has("MClass") then return to_mclass(model, node)
337 if node.labels.has("MClassDef") then return to_mclassdef(model, node)
338 if node.labels.has("MProperty") then return to_mproperty(model, node)
339 if node.labels.has("MPropDef") then return to_mpropdef(model, node)
340 if node.labels.has("MType") then return to_mtype(model, node)
341 if node.labels.has("MParameter") then return to_mparameter(model, node)
342 abort
343 end
344
345 # Build a new `MPackage` from a `node`.
346 #
347 # REQUIRE `node.labels.has("MPackage")`
348 private fun to_mpackage(model: Model, node: NeoNode): MPackage do
349 var m = mentities.get_or_null(node.id.as(Int))
350 if m isa MPackage then return m
351
352 assert node.labels.has("MPackage")
353 var location = to_location(node["location"].to_s)
354 var mpackage = new MPackage(node["name"].to_s, model, location)
355 mentities[node.id.as(Int)] = mpackage
356 set_doc(node, mpackage)
357 mpackage.root = to_mgroup(model, node.out_nodes("ROOT").first)
358 return mpackage
359 end
360
361 # Build a new `MGroup` from a `node`.
362 #
363 # REQUIRE `node.labels.has("MGroup")`
364 private fun to_mgroup(model: Model, node: NeoNode): MGroup do
365 var m = mentities.get_or_null(node.id.as(Int))
366 if m isa MGroup then return m
367
368 assert node.labels.has("MGroup")
369 var location = to_location(node["location"].to_s)
370 var mpackage = to_mpackage(model, node.out_nodes("PROJECT").first)
371 var parent: nullable MGroup = null
372 var out = node.out_nodes("PARENT")
373 if not out.is_empty then
374 parent = to_mgroup(model, out.first)
375 end
376 var mgroup = new MGroup(node["name"].to_s, location, mpackage, parent)
377 mentities[node.id.as(Int)] = mgroup
378 set_doc(node, mgroup)
379 return mgroup
380 end
381
382 # Build a new `MModule` from a `node`.
383 #
384 # REQUIRE `node.labels.has("MModule")`
385 private fun to_mmodule(model: Model, node: NeoNode): MModule do
386 var m = mentities.get_or_null(node.id.as(Int))
387 if m isa MModule then return m
388
389 assert node.labels.has("MModule")
390 var ins = node.in_nodes("DECLARES")
391 var mgroup: nullable MGroup = null
392 if not ins.is_empty then
393 mgroup = to_mgroup(model, ins.first)
394 end
395 var name = node["name"].to_s
396 var location = to_location(node["location"].to_s)
397 var mmodule = new MModule(model, mgroup, name, location)
398 mentities[node.id.as(Int)] = mmodule
399 set_doc(node, mmodule)
400 var imported_mmodules = new Array[MModule]
401 for smod in node.out_nodes("IMPORTS") do
402 imported_mmodules.add to_mmodule(model, smod)
403 end
404 mmodule.set_imported_mmodules(imported_mmodules)
405 return mmodule
406 end
407
408 # Build a new `MClass` from a `node`.
409 #
410 # REQUIRE `node.labels.has("MClass")`
411 private fun to_mclass(model: Model, node: NeoNode): MClass do
412 var m = mentities.get_or_null(node.id.as(Int))
413 if m isa MClass then return m
414
415 assert node.labels.has("MClass")
416 var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
417 var name = node["name"].to_s
418 var location = to_location(node["location"].to_s)
419 var kind = to_kind(node["kind"].to_s)
420 var visibility = to_visibility(node["visibility"].to_s)
421 var parameter_names = new Array[String]
422 if node.has_key("parameter_names") then
423 for e in node["parameter_names"].as(JsonArray) do
424 parameter_names.add e.to_s
425 end
426 end
427 var mclass = new MClass(mmodule, name, location, parameter_names, kind, visibility)
428 mentities[node.id.as(Int)] = mclass
429 set_doc(node, mclass)
430 return mclass
431 end
432
433 # Build a new `MClassDef` from a `node`.
434 #
435 # REQUIRE `node.labels.has("MClassDef")`
436 private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
437 var m = mentities.get_or_null(node.id.as(Int))
438 if m isa MClassDef then return m
439
440 assert node.labels.has("MClassDef")
441 var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
442 var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
443 var location = to_location(node["location"].to_s)
444 var mclassdef = new MClassDef(mmodule, mtype, location)
445 mentities[node.id.as(Int)] = mclassdef
446 set_doc(node, mclassdef)
447 var supertypes = new Array[MClassType]
448 for sup in node.out_nodes("INHERITS") do
449 supertypes.add to_mtype(model, sup).as(MClassType)
450 end
451 mclassdef.set_supertypes(supertypes)
452 mclassdef.add_in_hierarchy
453 return mclassdef
454 end
455
456 # Build a new `MProperty` from a `node`.
457 #
458 # REQUIRE `node.labels.has("MProperty")`
459 private fun to_mproperty(model: Model, node: NeoNode): MProperty do
460 var m = mentities.get_or_null(node.id.as(Int))
461 if m isa MProperty then return m
462
463 assert node.labels.has("MProperty")
464 var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
465 var name = node["name"].to_s
466 var location = to_location(node["location"].to_s)
467 var visibility = to_visibility(node["visibility"].to_s)
468 var mprop: nullable MProperty = null
469 if node.labels.has("MMethod") then
470 mprop = new MMethod(intro_mclassdef, name, location, visibility)
471 mprop.is_init = node["is_init"].as(Bool)
472 else if node.labels.has("MAttribute") then
473 mprop = new MAttribute(intro_mclassdef, name, location, visibility)
474 else if node.labels.has("MVirtualTypeProp") then
475 mprop = new MVirtualTypeProp(intro_mclassdef, name, location, visibility)
476 else if node.labels.has("MInnerClass") then
477 var inner = to_mclass(model, node.out_nodes("NESTS").first)
478 mprop = new MInnerClass(intro_mclassdef, name, location, visibility, inner)
479 end
480 if mprop == null then
481 print "not yet implemented to_mproperty for {node.labels.join(",")}"
482 abort
483 end
484 mentities[node.id.as(Int)] = mprop
485 set_doc(node, mprop)
486 return mprop
487 end
488
489 # Build a new `MPropDef` from a `node`.
490 #
491 # REQUIRE `node.labels.has("MPropDef")`
492 private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
493 var m = mentities.get_or_null(node.id.as(Int))
494 if m isa MPropDef then return m
495
496 assert node.labels.has("MPropDef")
497 var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
498 var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
499 var location = to_location(node["location"].to_s)
500 var mpropdef: nullable MPropDef = null
501 if node.labels.has("MMethodDef") then
502 mpropdef = new MMethodDef(mclassdef, mproperty.as(MMethod), location)
503 mpropdef.is_abstract = node["is_abstract"].as(Bool)
504 mpropdef.is_intern = node["is_intern"].as(Bool)
505 mpropdef.is_extern = node["is_extern"].as(Bool)
506 mentities[node.id.as(Int)] = mpropdef
507 mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
508 else if node.labels.has("MAttributeDef") then
509 mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
510 mentities[node.id.as(Int)] = mpropdef
511 var static_mtype = node.out_nodes("TYPE")
512 if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
513 else if node.labels.has("MVirtualTypeDef") then
514 mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
515 mentities[node.id.as(Int)] = mpropdef
516 var bound = node.out_nodes("BOUND")
517 if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
518 else if node.labels.has("MInnerClassDef") then
519 var inner = to_mclassdef(model, node.out_nodes("NESTS").first)
520 mpropdef = new MInnerClassDef(mclassdef,
521 mproperty.as(MInnerClass), location, inner)
522 mentities[node.id.as(Int)] = mpropdef
523 end
524 if mpropdef == null then
525 print "not yet implemented to_mpropdef for {node.labels.join(",")}"
526 abort
527 end
528 set_doc(node, mpropdef)
529 return mpropdef
530 end
531
532 # Build a new `MType` from a `node`.
533 #
534 # REQUIRE `node.labels.has("MType")`
535 private fun to_mtype(model: Model, node: NeoNode): MType do
536 var m = mentities.get_or_null(node.id.as(Int))
537 if m isa MType then return m
538
539 assert node.labels.has("MType")
540 if node.labels.has("MClassType") then
541 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
542 var args = new Array[MType]
543 for narg in node.out_nodes("ARGUMENT") do
544 args.add to_mtype(model, narg)
545 end
546 var mtype = mclass.get_mtype(args)
547 mentities[node.id.as(Int)] = mtype
548 return mtype
549 else if node.labels.has("MParameterType") then
550 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
551 var rank = node["rank"].to_s.to_i
552 var mtype = mclass.mparameters[rank]
553 mentities[node.id.as(Int)] = mtype
554 return mtype
555 else if node.labels.has("MNullableType") then
556 var intype = to_mtype(model, node.out_nodes("TYPE").first)
557 var mtype = intype.as_nullable
558 mentities[node.id.as(Int)] = mtype
559 return mtype
560 else if node.labels.has("MVirtualType") then
561 var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
562 assert mproperty isa MVirtualTypeProp
563 var mtype = mproperty.mvirtualtype
564 mentities[node.id.as(Int)] = mtype
565 return mtype
566 else if node.labels.has("MSignature") then
567 # Get all param nodes
568 var mparam_nodes = new HashMap[String, MParameter]
569 for pnode in node.out_nodes("PARAMETER") do
570 var mparam = to_mparameter(model, pnode)
571 mparam_nodes[mparam.name] = mparam
572 end
573 # Load params in the good order
574 var mparam_names = node["parameter_names"]
575 var mparameters = new Array[MParameter]
576 if mparam_names isa JsonArray then
577 for mparam_name in mparam_names do
578 var mparam = mparam_nodes[mparam_name.to_s]
579 mparameters.add mparam
580 end
581 end
582 var return_mtype: nullable MType = null
583 var ret_nodes = node.out_nodes("RETURNTYPE")
584 if not ret_nodes.is_empty then
585 return_mtype = to_mtype(model, ret_nodes.first)
586 end
587 var mtype = new MSignature(mparameters, return_mtype)
588 mentities[node.id.as(Int)] = mtype
589 return mtype
590 else if node.labels.has("MRawType") then
591 var mtype = new MRawType(model)
592 var parts = node["text"]
593 if parts isa JsonArray then
594 for p in parts do
595 mtype.parts.add(new MTypePart(model, p.to_s, null))
596 end
597 for pnode in node.out_nodes("LINK") do
598 assert pnode.labels.has("MTypePart")
599 if not pnode.out_nodes("TARGET").is_empty then
600 var rank = pnode["rank"]
601 var target = to_mentity(model, pnode.out_nodes("TARGET").first)
602 assert rank isa Int
603 mtype.parts[rank] = mtype.parts[rank].link_to(target)
604 end
605 end
606 end
607 mentities[node.id.as(Int)] = mtype
608 return mtype
609 end
610 print "not yet implemented to_mtype for {node.labels.join(",")}"
611 abort
612 end
613
614 # Build a new `MParameter` from `node`.
615 #
616 # REQUIRE `node.labels.has("MParameter")`
617 private fun to_mparameter(model: Model, node: NeoNode): MParameter do
618 var m = mentities.get_or_null(node.id.as(Int))
619 if m isa MParameter then return m
620
621 assert node.labels.has("MParameter")
622 var name = node["name"].to_s
623 var mtype = to_mtype(model, node.out_nodes("TYPE").first)
624 var is_vararg = node["is_vararg"].as(Bool)
625 var mparameter = new MParameter(name, mtype, is_vararg)
626 mentities[node.id.as(Int)] = mparameter
627 return mparameter
628 end
629
630 # Get a `Location` from its string representation.
631 private fun to_location(loc: String): nitc::Location do
632 return new nitc::Location.from_string(loc)
633 end
634
635 # Get a `MVisibility` from its string representation.
636 private fun to_visibility(vis: String): MVisibility do
637 if vis == intrude_visibility.to_s then
638 return intrude_visibility
639 else if vis == public_visibility.to_s then
640 return public_visibility
641 else if vis == protected_visibility.to_s then
642 return protected_visibility
643 else if vis == private_visibility.to_s then
644 return private_visibility
645 else if vis == package_visibility.to_s then
646 return package_visibility
647 else
648 return none_visibility
649 end
650 end
651
652 # Get a `MKind` from its string representation.
653 private fun to_kind(kind: String): MClassKind do
654 if kind == abstract_kind.to_s then
655 return abstract_kind
656 else if kind == concrete_kind.to_s then
657 return concrete_kind
658 else if kind == interface_kind.to_s then
659 return interface_kind
660 else if kind == enum_kind.to_s then
661 return enum_kind
662 else if kind == extern_kind.to_s then
663 return extern_kind
664 else
665 return raw_kind(kind)
666 end
667 end
668
669 # Extract the `MDoc` from `node` and link it to `mentity`.
670 private fun set_doc(node: NeoNode, mentity: MEntity) do
671 if node.has_key("mdoc") then
672 var lines = new Array[String]
673 for e in node["mdoc"].as(JsonArray) do
674 lines.add e.to_s#.replace("\n", "\\n")
675 end
676 var location = to_location(node["mdoc_location"].to_s)
677 var mdoc = new MDoc(location)
678 mdoc.content.add_all(lines)
679 mdoc.original_mentity = mentity
680 mentity.mdoc = mdoc
681 end
682 end
683 end
684
685 redef class MPackage
686 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
687 if nodes.has_key(self) then return nodes[self]
688 var node = super
689 var root = root
690 if root != null then
691 node.out_edges.add(new NeoEdge(node, "ROOT", root.to_node(nodes, model_name)))
692 end
693 return node
694 end
695 end
696
697 redef class MGroup
698 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
699 if nodes.has_key(self) then return nodes[self]
700 var node = super
701 var parent = parent
702 node.out_edges.add(new NeoEdge(node, "PROJECT", mpackage.to_node(nodes, model_name)))
703 if parent != null then
704 node.out_edges.add(new NeoEdge(node, "PARENT", parent.to_node(nodes, model_name)))
705 end
706 for mmodule in mmodules do
707 node.out_edges.add(new NeoEdge(node, "DECLARES", mmodule.to_node(nodes, model_name)))
708 end
709 for subgroup in in_nesting.direct_smallers do
710 node.in_edges.add(new NeoEdge(node, "NESTS", subgroup.to_node(nodes, model_name)))
711 end
712 return node
713 end
714 end
715
716 redef class MModule
717 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
718 if nodes.has_key(self) then return nodes[self]
719 var node = super
720 for parent in in_importation.direct_greaters do
721 node.out_edges.add(new NeoEdge(node, "IMPORTS", parent.to_node(nodes, model_name)))
722 end
723 for mclass in intro_mclasses do
724 node.out_edges.add(new NeoEdge(node, "INTRODUCES", mclass.to_node(nodes, model_name)))
725 end
726 for mclassdef in mclassdefs do
727 node.out_edges.add(new NeoEdge(node, "DEFINES", mclassdef.to_node(nodes, model_name)))
728 end
729 return node
730 end
731 end
732
733 redef class MClass
734 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
735 if nodes.has_key(self) then return nodes[self]
736 var node = super
737 node["kind"] = kind.to_s
738 node["visibility"] = visibility.to_s
739 if not mparameters.is_empty then
740 var parameter_names = new Array[String]
741 for p in mparameters do parameter_names.add(p.name)
742 node["parameter_names"] = new JsonArray.from(parameter_names)
743 end
744 node.out_edges.add(new NeoEdge(node, "CLASSTYPE", mclass_type.to_node(nodes, model_name)))
745 return node
746 end
747 end
748
749 redef class MClassDef
750 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
751 if nodes.has_key(self) then return nodes[self]
752 var node = super
753 node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", bound_mtype.to_node(nodes, model_name)))
754 node.out_edges.add(new NeoEdge(node, "MCLASS", mclass.to_node(nodes, model_name)))
755 for mproperty in intro_mproperties do
756 node.out_edges.add(new NeoEdge(node, "INTRODUCES", mproperty.to_node(nodes, model_name)))
757 end
758 for mpropdef in mpropdefs do
759 node.out_edges.add(new NeoEdge(node, "DECLARES", mpropdef.to_node(nodes, model_name)))
760 end
761 for sup in supertypes do
762 node.out_edges.add(new NeoEdge(node, "INHERITS", sup.to_node(nodes, model_name)))
763 end
764 return node
765 end
766 end
767
768 redef class MProperty
769 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
770 if nodes.has_key(self) then return nodes[self]
771 var node = make_node(nodes, model_name)
772 node.labels.add "MProperty"
773 node["visibility"] = visibility.to_s
774 node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", intro_mclassdef.to_node(nodes, model_name)))
775 node.labels.add self.class_name
776 return node
777 end
778 end
779
780 redef class MMethod
781 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
782 if nodes.has_key(self) then return nodes[self]
783 var node = super
784 node["is_init"] = is_init
785 return node
786 end
787 end
788
789 redef class MInnerClass
790 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
791 if nodes.has_key(self) then return nodes[self]
792 var node = super
793 node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
794 return node
795 end
796 end
797
798 redef class MPropDef
799 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
800 if nodes.has_key(self) then return nodes[self]
801 var node = make_node(nodes, model_name)
802 node.labels.add "MPropDef"
803 node.out_edges.add(new NeoEdge(node, "DEFINES", mproperty.to_node(nodes, model_name)))
804 node.labels.add self.class_name
805 return node
806 end
807 end
808
809 redef class MMethodDef
810 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
811 if nodes.has_key(self) then return nodes[self]
812 var node = super
813 node["is_abstract"] = is_abstract
814 node["is_intern"] = is_intern
815 node["is_extern"] = is_extern
816 var msignature = msignature
817 if msignature != null then
818 node.out_edges.add(new NeoEdge(node, "SIGNATURE", msignature.to_node(nodes, model_name)))
819 end
820 return node
821 end
822 end
823
824 redef class MAttributeDef
825 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
826 if nodes.has_key(self) then return nodes[self]
827 var node = super
828 var static_mtype = static_mtype
829 if static_mtype != null then
830 node.out_edges.add(new NeoEdge(node, "TYPE", static_mtype.to_node(nodes, model_name)))
831 end
832 return node
833 end
834 end
835
836 redef class MVirtualTypeDef
837 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
838 if nodes.has_key(self) then return nodes[self]
839 var node = super
840 var bound = bound
841 if bound != null then
842 node.out_edges.add(new NeoEdge(node, "BOUND", bound.to_node(nodes, model_name)))
843 end
844 return node
845 end
846 end
847
848 redef class MInnerClassDef
849 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
850 if nodes.has_key(self) then return nodes[self]
851 var node = super
852 node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
853 return node
854 end
855 end
856
857 redef class MTypePart
858 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
859 if nodes.has_key(self) then return nodes[self]
860 var node = super
861 if self.target != null then
862 var target_node = self.target.as(not null).to_node(nodes, model_name)
863 node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
864 end
865 return node
866 end
867 end
868
869 redef class MParameter
870 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
871 if nodes.has_key(self) then return nodes[self]
872 var node = super
873 node["name"] = self.name
874 node["is_vararg"] = self.is_vararg
875 node.out_edges.add(new NeoEdge(node, "TYPE", self.mtype.to_node(nodes, model_name)))
876 return node
877 end
878 end
879
880 redef class MClassType
881 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
882 if nodes.has_key(self) then return nodes[self]
883 var node = super
884 node.labels.add "MClassType"
885 node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
886 for arg in arguments do
887 node.out_edges.add(new NeoEdge(node, "ARGUMENT", arg.to_node(nodes, model_name)))
888 end
889 return node
890 end
891 end
892
893
894 redef class MGenericType
895 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
896 if nodes.has_key(self) then return nodes[self]
897 var node = super
898 node.labels.add "MGenericType"
899 return node
900 end
901 end
902
903 redef class MVirtualType
904 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
905 if nodes.has_key(self) then return nodes[self]
906 var node = super
907 node.labels.add "MVirtualType"
908 node.out_edges.add(new NeoEdge(node, "PROPERTY", mproperty.to_node(nodes, model_name)))
909 return node
910 end
911 end
912
913 redef class MParameterType
914 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
915 if nodes.has_key(self) then return nodes[self]
916 var node = super
917 node.labels.add "MParameterType"
918 node["rank"] = rank
919 node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
920 return node
921 end
922 end
923
924 redef class MNullableType
925 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
926 if nodes.has_key(self) then return nodes[self]
927 var node = super
928 node.labels.add "MNullableType"
929 node.out_edges.add(new NeoEdge(node, "TYPE", mtype.to_node(nodes, model_name)))
930 return node
931 end
932 end
933
934 redef class MSignature
935 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
936 if nodes.has_key(self) then return nodes[self]
937 var node = super
938 node.labels.add "MSignature"
939 var names = new JsonArray
940 var rank = 0
941 for mparameter in mparameters do
942 names.add mparameter.name
943 var pnode = mparameter.to_node(nodes, model_name)
944 pnode["rank"] = rank
945 node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
946 rank += 1
947 end
948 if not names.is_empty then node["parameter_names"] = names
949 var return_mtype = return_mtype
950 if return_mtype != null then
951 node.out_edges.add(new NeoEdge(node, "RETURNTYPE", return_mtype.to_node(nodes, model_name)))
952 end
953 return node
954 end
955 end
956
957 redef class MRawType
958 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
959 if nodes.has_key(self) then return nodes[self]
960 var node = super
961 node.labels.add "MRawType"
962 var text = new JsonArray
963 var rank = 0
964 for part in self.parts do
965 text.add part.text
966 if part.target != null then
967 var pnode = part.to_node(nodes, model_name)
968 pnode["rank"] = rank
969 node.out_edges.add(new NeoEdge(node, "LINK", pnode))
970 end
971 rank += 1
972 end
973 if not text.is_empty then node["text"] = text
974 return node
975 end
976 end
977
978 redef class MType
979 redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
980 if nodes.has_key(self) then return nodes[self]
981 var node = make_node(nodes, model_name)
982 node.labels.add "MType"
983 return node
984 end
985 end
986
987 redef class MEntity
988 # Build a `NeoNode` representing `self`.
989 private fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
990 if nodes.has_key(self) then return nodes[self]
991 var node = make_node(nodes, model_name)
992 node.labels.add self.class_name
993 return node
994 end
995
996 # Make a new `NeoNode` based on `mentity`.
997 private fun make_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
998 var node = new NeoNode
999 nodes[self] = node
1000 node.labels.add "MEntity"
1001 if model_name != null then node.labels.add model_name
1002 node["name"] = self.name
1003 if not self isa MSignature then
1004 #FIXME: MSignature is a MEntity, but has no model :/
1005 node["location"] = self.location.to_s
1006 end
1007 var mdoc = self.mdoc
1008 if mdoc != null then
1009 node["mdoc"] = new JsonArray.from(mdoc.content)
1010 node["mdoc_location"] = mdoc.location.to_s
1011 end
1012 return node
1013 end
1014 end