neo: Remove `full_name`.
[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 # `MProject`
39 #
40 # * labels: `MProject`, `model_name` and `MEntity`.
41 # * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree.
42 #
43 # `MGroup`
44 #
45 # * labels: `MGroup`, `model_name` and `MEntity`.
46 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
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 project node...", 1)
237 nodes = client.nodes_with_labels([model_name, "MProject"])
238 for node in nodes do to_mproject(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 mproject in model.mprojects do
308 to_node(mproject)
309 for mgroup in mproject.mgroups do to_node(mgroup)
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 `NeoNode` associated with `mentity`.
332 # `mentities` are stored locally to avoid duplication.
333 fun to_node(mentity: MEntity): NeoNode do
334 if nodes.has_key(mentity) then return nodes[mentity]
335 if mentity isa MProject then return mproject_node(mentity)
336 if mentity isa MGroup then return mgroup_node(mentity)
337 if mentity isa MModule then return mmodule_node(mentity)
338 if mentity isa MClass then return mclass_node(mentity)
339 if mentity isa MClassDef then return mclassdef_node(mentity)
340 if mentity isa MProperty then return mproperty_node(mentity)
341 if mentity isa MPropDef then return mpropdef_node(mentity)
342 if mentity isa MType then return mtype_node(mentity)
343 if mentity isa MParameter then return mparameter_node(mentity)
344 abort
345 end
346
347 # Get the `MEntity` associated with `node`.
348 fun to_mentity(model: Model, node: NeoNode): MEntity do
349 if node.labels.has("MProject") then return to_mproject(model, node)
350 if node.labels.has("MGroup") then return to_mgroup(model, node)
351 if node.labels.has("MModule") then return to_mmodule(model, node)
352 if node.labels.has("MClass") then return to_mclass(model, node)
353 if node.labels.has("MClassDef") then return to_mclassdef(model, node)
354 if node.labels.has("MProperty") then return to_mproperty(model, node)
355 if node.labels.has("MPropDef") then return to_mpropdef(model, node)
356 if node.labels.has("MType") then return to_mtype(model, node)
357 if node.labels.has("MParameter") then return to_mparameter(model, node)
358 abort
359 end
360
361 # Make a new `NeoNode` based on `mentity`.
362 private fun make_node(mentity: MEntity): NeoNode do
363 var node = new NeoNode
364 nodes[mentity] = node
365 node.labels.add "MEntity"
366 node.labels.add model_name
367 node["name"] = mentity.name
368 if mentity.mdoc != null then node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
369 return node
370 end
371
372 # Build a `NeoNode` representing `mproject`.
373 private fun mproject_node(mproject: MProject): NeoNode do
374 var node = make_node(mproject)
375 node.labels.add "MProject"
376 var root = mproject.root
377 if root != null then
378 node.out_edges.add(new NeoEdge(node, "ROOT", to_node(root)))
379 end
380 return node
381 end
382
383 # Build a new `MProject` from a `node`.
384 #
385 # REQUIRE `node.labels.has("MProject")`
386 private fun to_mproject(model: Model, node: NeoNode): MProject do
387 var m = mentities.get_or_null(node.id.as(Int))
388 if m isa MProject then return m
389
390 assert node.labels.has("MProject")
391 var mproject = new MProject(node["name"].to_s, model)
392 mentities[node.id.as(Int)] = mproject
393 set_doc(node, mproject)
394 mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
395 return mproject
396 end
397
398 # Build a `NeoNode` representing `mgroup`.
399 private fun mgroup_node(mgroup: MGroup): NeoNode do
400 var node = make_node(mgroup)
401 node.labels.add "MGroup"
402 var parent = mgroup.parent
403 node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mproject)))
404 if parent != null then
405 node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
406 end
407 for mmodule in mgroup.mmodules do
408 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mmodule)))
409 end
410 for subgroup in mgroup.in_nesting.direct_smallers do
411 node.in_edges.add(new NeoEdge(node, "NESTS", to_node(subgroup)))
412 end
413 return node
414 end
415
416 # Build a new `MGroup` from a `node`.
417 #
418 # REQUIRE `node.labels.has("MGroup")`
419 private fun to_mgroup(model: Model, node: NeoNode): MGroup do
420 var m = mentities.get_or_null(node.id.as(Int))
421 if m isa MGroup then return m
422
423 assert node.labels.has("MGroup")
424 var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
425 var parent: nullable MGroup = null
426 var out = node.out_nodes("PARENT")
427 if not out.is_empty then
428 parent = to_mgroup(model, out.first)
429 end
430 var mgroup = new MGroup(node["name"].to_s, mproject, parent)
431 mentities[node.id.as(Int)] = mgroup
432 set_doc(node, mgroup)
433 return mgroup
434 end
435
436 # Build a `NeoNode` representing `mmodule`.
437 private fun mmodule_node(mmodule: MModule): NeoNode do
438 var node = make_node(mmodule)
439 node.labels.add "MModule"
440 node["location"] = mmodule.location.to_s
441 for parent in mmodule.in_importation.direct_greaters do
442 node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
443 end
444 for mclass in mmodule.intro_mclasses do
445 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mclass)))
446 end
447 for mclassdef in mmodule.mclassdefs do
448 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mclassdef)))
449 end
450 return node
451 end
452
453 # Build a new `MModule` from a `node`.
454 #
455 # REQUIRE `node.labels.has("MModule")`
456 private fun to_mmodule(model: Model, node: NeoNode): MModule do
457 var m = mentities.get_or_null(node.id.as(Int))
458 if m isa MModule then return m
459
460 assert node.labels.has("MModule")
461 var ins = node.in_nodes("DECLARES")
462 var mgroup: nullable MGroup = null
463 if not ins.is_empty then
464 mgroup = to_mgroup(model, ins.first)
465 end
466 var name = node["name"].to_s
467 var location = to_location(node["location"].to_s)
468 var mmodule = new MModule(model, mgroup, name, location)
469 mentities[node.id.as(Int)] = mmodule
470 set_doc(node, mmodule)
471 var imported_mmodules = new Array[MModule]
472 for smod in node.out_nodes("IMPORTS") do
473 imported_mmodules.add to_mmodule(model, smod)
474 end
475 mmodule.set_imported_mmodules(imported_mmodules)
476 return mmodule
477 end
478
479 # Build a `NeoNode` representing `mclass`.
480 private fun mclass_node(mclass: MClass): NeoNode do
481 var node = make_node(mclass)
482 node.labels.add "MClass"
483 node["kind"] = mclass.kind.to_s
484 node["visibility"] = mclass.visibility.to_s
485 if not mclass.mparameters.is_empty then
486 var parameter_names = new Array[String]
487 for p in mclass.mparameters do parameter_names.add(p.name)
488 node["parameter_names"] = new JsonArray.from(parameter_names)
489 end
490 node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type)))
491 return node
492 end
493
494 # Build a new `MClass` from a `node`.
495 #
496 # REQUIRE `node.labels.has("MClass")`
497 private fun to_mclass(model: Model, node: NeoNode): MClass do
498 var m = mentities.get_or_null(node.id.as(Int))
499 if m isa MClass then return m
500
501 assert node.labels.has("MClass")
502 var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
503 var name = node["name"].to_s
504 var kind = to_kind(node["kind"].to_s)
505 var visibility = to_visibility(node["visibility"].to_s)
506 var parameter_names = new Array[String]
507 if node.has_key("parameter_names") then
508 for e in node["parameter_names"].as(JsonArray) do
509 parameter_names.add e.to_s
510 end
511 end
512 var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
513 mentities[node.id.as(Int)] = mclass
514 set_doc(node, mclass)
515 return mclass
516 end
517
518 # Build a `NeoNode` representing `mclassdef`.
519 private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
520 var node = make_node(mclassdef)
521 node.labels.add "MClassDef"
522 node["location"] = mclassdef.location.to_s
523 node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
524 node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
525 for mproperty in mclassdef.intro_mproperties do
526 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mproperty)))
527 end
528 for mpropdef in mclassdef.mpropdefs do
529 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mpropdef)))
530 end
531 for sup in mclassdef.supertypes do
532 node.out_edges.add(new NeoEdge(node, "INHERITS", to_node(sup)))
533 end
534 return node
535 end
536
537 # Build a new `MClassDef` from a `node`.
538 #
539 # REQUIRE `node.labels.has("MClassDef")`
540 private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
541 var m = mentities.get_or_null(node.id.as(Int))
542 if m isa MClassDef then return m
543
544 assert node.labels.has("MClassDef")
545 var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
546 var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
547 var location = to_location(node["location"].to_s)
548 var mclassdef = new MClassDef(mmodule, mtype, location)
549 mentities[node.id.as(Int)] = mclassdef
550 set_doc(node, mclassdef)
551 var supertypes = new Array[MClassType]
552 for sup in node.out_nodes("INHERITS") do
553 supertypes.add to_mtype(model, sup).as(MClassType)
554 end
555 mclassdef.set_supertypes(supertypes)
556 mclassdef.add_in_hierarchy
557 return mclassdef
558 end
559
560 # Build a `NeoNode` representing `mproperty`.
561 private fun mproperty_node(mproperty: MProperty): NeoNode do
562 var node = make_node(mproperty)
563 node.labels.add "MProperty"
564 node["visibility"] = mproperty.visibility.to_s
565 if mproperty isa MMethod then
566 node.labels.add "MMethod"
567 node["is_init"] = mproperty.is_init
568 else if mproperty isa MAttribute then
569 node.labels.add "MAttribute"
570 else if mproperty isa MVirtualTypeProp then
571 node.labels.add "MVirtualTypeProp"
572 else if mproperty isa MInnerClass then
573 node.labels.add "MInnerClass"
574 node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mproperty.inner)))
575 end
576 node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
577 return node
578 end
579
580 # Build a new `MProperty` from a `node`.
581 #
582 # REQUIRE `node.labels.has("MProperty")`
583 private fun to_mproperty(model: Model, node: NeoNode): MProperty do
584 var m = mentities.get_or_null(node.id.as(Int))
585 if m isa MProperty then return m
586
587 assert node.labels.has("MProperty")
588 var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
589 var name = node["name"].to_s
590 var visibility = to_visibility(node["visibility"].to_s)
591 var mprop: nullable MProperty = null
592 if node.labels.has("MMethod") then
593 mprop = new MMethod(intro_mclassdef, name, visibility)
594 mprop.is_init = node["is_init"].as(Bool)
595 else if node.labels.has("MAttribute") then
596 mprop = new MAttribute(intro_mclassdef, name, visibility)
597 else if node.labels.has("MVirtualTypeProp") then
598 mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
599 else if node.labels.has("MInnerClass") then
600 var inner = to_mclass(model, node.out_nodes("NESTS").first)
601 mprop = new MInnerClass(intro_mclassdef, name, visibility, inner)
602 end
603 if mprop == null then
604 print "not yet implemented to_mproperty for {node.labels.join(",")}"
605 abort
606 end
607 mentities[node.id.as(Int)] = mprop
608 set_doc(node, mprop)
609 return mprop
610 end
611
612 # Build a `NeoNode` representing `mpropdef`.
613 private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
614 var node = make_node(mpropdef)
615 node.labels.add "MPropDef"
616 node["location"] = mpropdef.location.to_s
617 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
618 if mpropdef isa MMethodDef then
619 node.labels.add "MMethodDef"
620 node["is_abstract"] = mpropdef.is_abstract
621 node["is_intern"] = mpropdef.is_intern
622 node["is_extern"] = mpropdef.is_extern
623 var msignature = mpropdef.msignature
624 if msignature != null then
625 node.out_edges.add(new NeoEdge(node, "SIGNATURE", to_node(msignature)))
626 end
627 else if mpropdef isa MAttributeDef then
628 node.labels.add "MAttributeDef"
629 var static_mtype = mpropdef.static_mtype
630 if static_mtype != null then
631 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype)))
632 end
633 else if mpropdef isa MVirtualTypeDef then
634 node.labels.add "MVirtualTypeDef"
635 var bound = mpropdef.bound
636 if bound != null then
637 node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
638 end
639 else if mpropdef isa MInnerClassDef then
640 node.labels.add "MInnerClassDef"
641 node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mpropdef.inner)))
642 end
643 return node
644 end
645
646 # Build a new `MPropDef` from a `node`.
647 #
648 # REQUIRE `node.labels.has("MPropDef")`
649 private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
650 var m = mentities.get_or_null(node.id.as(Int))
651 if m isa MPropDef then return m
652
653 assert node.labels.has("MPropDef")
654 var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
655 var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
656 var location = to_location(node["location"].to_s)
657 var mpropdef: nullable MPropDef = null
658 if node.labels.has("MMethodDef") then
659 mpropdef = new MMethodDef(mclassdef, mproperty.as(MMethod), location)
660 mpropdef.is_abstract = node["is_abstract"].as(Bool)
661 mpropdef.is_intern = node["is_intern"].as(Bool)
662 mpropdef.is_extern = node["is_extern"].as(Bool)
663 mentities[node.id.as(Int)] = mpropdef
664 mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
665 else if node.labels.has("MAttributeDef") then
666 mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
667 mentities[node.id.as(Int)] = mpropdef
668 var static_mtype = node.out_nodes("TYPE")
669 if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
670 else if node.labels.has("MVirtualTypeDef") then
671 mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
672 mentities[node.id.as(Int)] = mpropdef
673 var bound = node.out_nodes("BOUND")
674 if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
675 else if node.labels.has("MInnerClassDef") then
676 var inner = to_mclassdef(model, node.out_nodes("NESTS").first)
677 mpropdef = new MInnerClassDef(mclassdef,
678 mproperty.as(MInnerClass), location, inner)
679 mentities[node.id.as(Int)] = mpropdef
680 end
681 if mpropdef == null then
682 print "not yet implemented to_mpropdef for {node.labels.join(",")}"
683 abort
684 end
685 set_doc(node, mpropdef)
686 return mpropdef
687 end
688
689 # Build a `NeoNode` representing `mtype`.
690 private fun mtype_node(mtype: MType): NeoNode do
691 var node = make_node(mtype)
692 node.labels.add "MType"
693 if mtype isa MClassType then
694 node.labels.add "MClassType"
695 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
696 for arg in mtype.arguments do
697 node.out_edges.add(new NeoEdge(node, "ARGUMENT", to_node(arg)))
698 end
699 if mtype isa MGenericType then
700 node.labels.add "MGenericType"
701 end
702 else if mtype isa MVirtualType then
703 node.labels.add "MVirtualType"
704 node.out_edges.add(new NeoEdge(node, "PROPERTY", to_node(mtype.mproperty)))
705 else if mtype isa MParameterType then
706 node.labels.add "MParameterType"
707 node["rank"] = mtype.rank
708 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
709 else if mtype isa MNullableType then
710 node.labels.add "MNullableType"
711 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype)))
712 else if mtype isa MSignature then
713 node.labels.add "MSignature"
714 var names = new JsonArray
715 var rank = 0
716 for mparameter in mtype.mparameters do
717 names.add mparameter.name
718 var pnode = mparameter_node(mparameter)
719 pnode["rank"] = rank
720 node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
721 rank += 1
722 end
723 if not names.is_empty then node["parameter_names"] = names
724 var return_mtype = mtype.return_mtype
725 if return_mtype != null then
726 node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
727 end
728 else if mtype isa MRawType then
729 node.labels.add "MRawType"
730 var text = new JsonArray
731 var rank = 0
732 for part in mtype.parts do
733 text.add part.text
734 if part.target != null then
735 var pnode = mtypepart_node(part)
736 pnode["rank"] = rank
737 node.out_edges.add(new NeoEdge(node, "LINK", pnode))
738 end
739 rank += 1
740 end
741 if not text.is_empty then node["text"] = text
742 end
743 return node
744 end
745
746 # Build a `NeoNode` representing `mtypepart`.
747 private fun mtypepart_node(mtypepart: MTypePart): NeoNode do
748 var node = make_node(mtypepart)
749 node.labels.add "MTypePart"
750 if mtypepart.target != null then
751 var target_node = to_node(mtypepart.target.as(not null))
752 node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
753 end
754 return node
755 end
756
757 # Build a new `MType` from a `node`.
758 #
759 # REQUIRE `node.labels.has("MType")`
760 private fun to_mtype(model: Model, node: NeoNode): MType do
761 var m = mentities.get_or_null(node.id.as(Int))
762 if m isa MType then return m
763
764 assert node.labels.has("MType")
765 if node.labels.has("MClassType") then
766 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
767 var args = new Array[MType]
768 for narg in node.out_nodes("ARGUMENT") do
769 args.add to_mtype(model, narg)
770 end
771 var mtype = mclass.get_mtype(args)
772 mentities[node.id.as(Int)] = mtype
773 return mtype
774 else if node.labels.has("MParameterType") then
775 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
776 var rank = node["rank"].to_s.to_i
777 var mtype = mclass.mparameters[rank]
778 mentities[node.id.as(Int)] = mtype
779 return mtype
780 else if node.labels.has("MNullableType") then
781 var intype = to_mtype(model, node.out_nodes("TYPE").first)
782 var mtype = intype.as_nullable
783 mentities[node.id.as(Int)] = mtype
784 return mtype
785 else if node.labels.has("MVirtualType") then
786 var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
787 assert mproperty isa MVirtualTypeProp
788 var mtype = mproperty.mvirtualtype
789 mentities[node.id.as(Int)] = mtype
790 return mtype
791 else if node.labels.has("MSignature") then
792 # Get all param nodes
793 var mparam_nodes = new HashMap[String, MParameter]
794 for pnode in node.out_nodes("PARAMETER") do
795 var mparam = to_mparameter(model, pnode)
796 mparam_nodes[mparam.name] = mparam
797 end
798 # Load params in the good order
799 var mparam_names = node["parameter_names"]
800 var mparameters = new Array[MParameter]
801 if mparam_names isa JsonArray then
802 for mparam_name in mparam_names do
803 var mparam = mparam_nodes[mparam_name.to_s]
804 mparameters.add mparam
805 end
806 end
807 var return_mtype: nullable MType = null
808 var ret_nodes = node.out_nodes("RETURNTYPE")
809 if not ret_nodes.is_empty then
810 return_mtype = to_mtype(model, ret_nodes.first)
811 end
812 var mtype = new MSignature(mparameters, return_mtype)
813 mentities[node.id.as(Int)] = mtype
814 return mtype
815 else if node.labels.has("MRawType") then
816 var mtype = new MRawType(model)
817 var parts = node["text"]
818 if parts isa JsonArray then
819 for p in parts do
820 mtype.parts.add(new MTypePart(model, p.to_s, null))
821 end
822 for pnode in node.out_nodes("LINK") do
823 assert pnode.labels.has("MTypePart")
824 if not pnode.out_nodes("TARGET").is_empty then
825 var rank = pnode["rank"]
826 var target = to_mentity(model, pnode.out_nodes("TARGET").first)
827 assert rank isa Int
828 mtype.parts[rank] = mtype.parts[rank].link_to(target)
829 end
830 end
831 end
832 mentities[node.id.as(Int)] = mtype
833 return mtype
834 end
835 print "not yet implemented to_mtype for {node.labels.join(",")}"
836 abort
837 end
838
839 # Build a `NeoNode` representing `mparameter`.
840 private fun mparameter_node(mparameter: MParameter): NeoNode do
841 var node = make_node(mparameter)
842 node.labels.add "MParameter"
843 node["name"] = mparameter.name
844 node["is_vararg"] = mparameter.is_vararg
845 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
846 return node
847 end
848
849 # Build a new `MParameter` from `node`.
850 #
851 # REQUIRE `node.labels.has("MParameter")`
852 private fun to_mparameter(model: Model, node: NeoNode): MParameter do
853 var m = mentities.get_or_null(node.id.as(Int))
854 if m isa MParameter then return m
855
856 assert node.labels.has("MParameter")
857 var name = node["name"].to_s
858 var mtype = to_mtype(model, node.out_nodes("TYPE").first)
859 var is_vararg = node["is_vararg"].as(Bool)
860 var mparameter = new MParameter(name, mtype, is_vararg)
861 mentities[node.id.as(Int)] = mparameter
862 return mparameter
863 end
864
865 # Get a `Location` from its string representation.
866 private fun to_location(loc: String): Location do
867 #TODO filepath
868 var parts = loc.split_with(":")
869 var file = new SourceFile.from_string(parts[0], "")
870 var pos = parts[1].split_with("--")
871 var pos1 = pos[0].split_with(",")
872 var pos2 = pos[1].split_with(",")
873 var line_s = pos1[0].to_i
874 var line_e = pos2[0].to_i
875 var column_s = pos1[1].to_i
876 var column_e = 0
877 if pos2.length == 2 then pos2[1].to_i
878 return new Location(file, line_s, line_e, column_s, column_e)
879 end
880
881 # Get a `MVisibility` from its string representation.
882 private fun to_visibility(vis: String): MVisibility do
883 if vis == intrude_visibility.to_s then
884 return intrude_visibility
885 else if vis == public_visibility.to_s then
886 return public_visibility
887 else if vis == protected_visibility.to_s then
888 return protected_visibility
889 else if vis == private_visibility.to_s then
890 return private_visibility
891 else if vis == package_visibility.to_s then
892 return package_visibility
893 else
894 return none_visibility
895 end
896 end
897
898 # Get a `MKind` from its string representation.
899 private fun to_kind(kind: String): MClassKind do
900 if kind == abstract_kind.to_s then
901 return abstract_kind
902 else if kind == concrete_kind.to_s then
903 return concrete_kind
904 else if kind == interface_kind.to_s then
905 return interface_kind
906 else if kind == enum_kind.to_s then
907 return enum_kind
908 else if kind == extern_kind.to_s then
909 return extern_kind
910 else
911 return raw_kind(kind)
912 end
913 end
914
915 # Extract the `MDoc` from `node` and link it to `mentity`.
916 private fun set_doc(node: NeoNode, mentity: MEntity) do
917 if node.has_key("mdoc") then
918 var lines = new Array[String]
919 for e in node["mdoc"].as(JsonArray) do
920 lines.add e.to_s#.replace("\n", "\\n")
921 end
922 var mdoc = new MDoc
923 mdoc.content.add_all(lines)
924 mdoc.original_mentity = mentity
925 mentity.mdoc = mdoc
926 end
927 end
928 end