1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Save and load `Model` from/to Neo4j base.
17 # Nit models are composed by MEntities.
18 # This module creates NeoNode for each MEntity found in a `Model` and save them into Neo4j database.
22 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
24 # Structure of the nit `Model` in base:
26 # For any `MEntity` (in addition to specific data):
28 # * Generic labels: model name (`model_name`) and `MEntity`.
29 # * `name`: short (unqualified) name.
30 # * `mdoc`: JSON array representing the associated Markdown documentation
31 # (one item by line). Does not exist if there is no associated documentation.
33 # Note : All nodes in the model are `MEntity`.
37 # * Specific label: `MProject`
38 # * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree.
42 # * Specific label: `MGroup`
43 # * `full_name`: fully qualified name.
44 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
45 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root group.
46 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of this group.
47 # * `(:MGroup)-[:NESTS]->(:MGroup)`: subgroups that are direct children of this group.
51 # * Specific label: `MModule`
52 # * `full_name`: fully qualified name.
53 # * `location`: origin of the definition. See `Location.to_s`.
54 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
55 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this module.
56 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in this module.
60 # * Specific label: `MClass`
61 # * `full_name`: fully qualified name.
62 # * `arity`: number of generic formal parameters. 0 if the class is not generic.
63 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
64 # * `visibility`: visibility of the class.
65 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: principal static type of the class.
69 # * Specific label: `MClassDef`
70 # * `is_intro`: indicates if this definition introduces the class.
71 # * `location`: origin of the definition. See `Location.to_s`.
72 # * `parameter_names`: JSON array listing the name of each formal generic
73 # parameter (in order of declaration). Does not exist if there is no formal
75 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the classdef.
76 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
77 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by the classdef.
78 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the classdef (introductions and redefinitions).
79 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
83 # * Specific label: `MProperty`. Must also have `MMethod`, `MAttribute` or
84 # `MVirtualTypeProp`, depending on the class of the represented entity.
85 # * `full_name`: fully qualified name.
86 # * `visibility`: visibility of the property.
87 # * `is_init`: Indicates if the property is a constructor. Exists only if the node is a `MMethod`.
88 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduce the property.
92 # * Specific label: `MPropDef`. Must also have `MMethodDef`, `MAttributeDef` or
93 # `MVirtualTypeDef`, depending on the class of the represented entity.
94 # * `is_intro`: indicates if this definition introduces the property.
95 # * `location`: origin of the definition. See `Location.to_s`.
96 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
98 # Additional attributes and relationship for `MMethodDef`:
100 # * `is_abstract`: Is the method definition abstract?
101 # * `is_intern`: Is the method definition intern?
102 # * `is_extern`: Is the method definition extern?
103 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the property definition.
105 # Additional relationship for `MVirtualTypeDef`:
107 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
108 # is bound in this definition. Exists only if this definition bound the virtual
109 # type to an effective type.
113 # * Specific label: `MType`. Must also have `MClassType`, `MNullableType`,
114 # `MVirtualType` or `MSignature`, depending on the class of the represented
117 # Additional label and relationships for `MClassType`:
119 # * If it is a `MGenericType`, has also the `MGenericType` label.
120 # * `(:MClassType)-[:CLASS]->(:MClass)`: class.
121 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
123 # Additional relationship for `MVirtualType`:
125 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
126 # determines the type (usually a `MVirtualTypeProp`).
128 # Additional attribute and relationship for `MParameterType`:
130 # * `rank`: position of the parameter (0 for the first parameter).
131 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter belong.
133 # Additional relationship for `MNullableType`:
135 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
137 # Additional attribute and relationships for `MSignature`:
139 # * `parameter_names`: JSON array representing the list of the parameter names.
140 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
141 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
144 # In order to maintain the correct parameters order, each `MSignature` node contains
145 # an array of parameter names corresponding to the parameter order in the signature.
147 # For example, if the source code contains:
149 # fun foo(a: A, b: B, c: C)
151 # The `MSignature` node will contain a property `parameter_names = ["a", "b", "c"]` so
152 # the MSignature can be reconstructed with the parameters in the correct order.
156 # * Specific label: `MParameter`
157 # * `is_vararg`: Is the parameter a vararg?
158 # * `rank`: position of the parameter (0 for the first parameter).
159 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
161 # MParameters are also ranked by their position in the corresponding signature.
162 # Rank 0 for the first parameter, 1 for the next one and etc.
169 # Helper class that can save and load a `Model` into a Neo4j database.
174 # Because we use only one Neo4j instance to store all the models,
175 # we need to mark their appartenance to a particular model and avoid loading all models.
177 # The name is used as a Neo label on each created nodes and used to load nodes from base.
178 var model_name
: String
180 # The toolcontext used to init the `NeoModel` tool.
181 var toolcontext
: ToolContext
183 # The Neo4j `client` used to communicate with the Neo4j instance.
184 var client
: Neo4jClient
186 # Fill `model` using base pointed by `client`.
187 fun load
(model
: Model): Model do
188 toolcontext
.info
("Locate all mentities...", 1)
189 var nodes
= client
.nodes_with_label
(model_name
)
191 toolcontext
.info
("Preload nodes...", 1)
192 pull_all_nodes
(nodes
)
193 toolcontext
.info
("Preload edges...", 1)
194 pull_all_edges
(nodes
)
196 toolcontext
.info
("Build model...", 1)
197 nodes
= client
.nodes_with_labels
([model_name
, "MProject"])
198 for node
in nodes
do to_mproject
(model
, node
)
199 nodes
= client
.nodes_with_labels
([model_name
, "MGroup"])
200 for node
in nodes
do to_mgroup
(model
, node
)
201 nodes
= client
.nodes_with_labels
([model_name
, "MModule"])
202 for node
in nodes
do to_mmodule
(model
, node
)
203 nodes
= client
.nodes_with_labels
([model_name
, "MClass"])
204 for node
in nodes
do to_mclass
(model
, node
)
205 nodes
= client
.nodes_with_labels
([model_name
, "MClassDef"])
206 for node
in nodes
do to_mclassdef
(model
, node
)
207 nodes
= client
.nodes_with_labels
([model_name
, "MProperty"])
208 for node
in nodes
do to_mproperty
(model
, node
)
209 nodes
= client
.nodes_with_labels
([model_name
, "MPropDef"])
210 for node
in nodes
do to_mpropdef
(model
, node
)
214 # Save `model` in the base pointed by `client`.
215 fun save
(model
: Model) do
216 var nodes
= collect_model_nodes
(model
)
217 toolcontext
.info
("Save {nodes.length} nodes...", 1)
219 var edges
= collect_model_edges
(model
)
220 toolcontext
.info
("Save {edges.length} edges...", 1)
224 # Save `neo_entities` in base using batch mode.
225 private fun push_all
(neo_entities
: Collection[NeoEntity]) do
226 var batch
= new NeoBatch(client
)
227 var len
= neo_entities
.length
230 for nentity
in neo_entities
do
231 batch
.save_entity
(nentity
)
232 if i
== batch_max_size
then
234 sum
+= batch_max_size
235 toolcontext
.info
(" {sum * 100 / len}% done", 1)
236 batch
= new NeoBatch(client
)
245 # Load content for all `nodes` from base.
247 # Content corresponds to properties and labels that are loaded in batch mode.
248 private fun pull_all_nodes
(nodes
: Collection[NeoNode]) do
249 var batch
= new NeoBatch(client
)
250 var len
= nodes
.length
254 batch
.load_node
(node
)
255 if i
== batch_max_size
then
257 sum
+= batch_max_size
258 toolcontext
.info
(" {sum * 100 / len}% done", 1)
259 batch
= new NeoBatch(client
)
268 # Load all edges from base linked to `nodes`.
270 # Edges are loaded in batch mode.
271 private fun pull_all_edges
(nodes
: Collection[NeoNode]) do
272 var batch
= new NeoBatch(client
)
273 var len
= nodes
.length
277 batch
.load_node_edges
(node
)
278 if i
== batch_max_size
then
280 sum
+= batch_max_size
281 toolcontext
.info
(" {sum * 100 / len}% done", 1)
282 batch
= new NeoBatch(client
)
291 # How many operation can be executed in one batch?
292 private var batch_max_size
= 1000
294 # Execute `batch` and check for errors.
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
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
309 for mgroup
in mproject
.mgroups
do to_node
(mgroup
)
314 # Collect all edges from the current `model`.
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
)
323 # Mentities associated to nodes.
324 private var mentities
= new HashMap[NeoNode, MEntity]
326 # Nodes associated with MEntities.
327 private var nodes
= new HashMap[MEntity, NeoNode]
329 # Get the `NeoNode` associated with `mentity`.
330 # `mentities` are stored locally to avoid duplication.
331 fun to_node
(mentity
: MEntity): NeoNode do
332 if nodes
.has_key
(mentity
) then return nodes
[mentity
]
333 if mentity
isa MProject then return mproject_node
(mentity
)
334 if mentity
isa MGroup then return mgroup_node
(mentity
)
335 if mentity
isa MModule then return mmodule_node
(mentity
)
336 if mentity
isa MClass then return mclass_node
(mentity
)
337 if mentity
isa MClassDef then return mclassdef_node
(mentity
)
338 if mentity
isa MProperty then return mproperty_node
(mentity
)
339 if mentity
isa MPropDef then return mpropdef_node
(mentity
)
340 if mentity
isa MType then return mtype_node
(mentity
)
341 if mentity
isa MParameter then return mparameter_node
(mentity
)
345 # Make a new `NeoNode` based on `mentity`.
346 private fun make_node
(mentity
: MEntity): NeoNode do
347 var node
= new NeoNode
348 nodes
[mentity
] = node
349 node
.labels
.add
"MEntity"
350 node
.labels
.add model_name
351 node
["name"] = mentity
.name
352 if mentity
.mdoc
!= null then node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
356 # Build a `NeoNode` representing `mproject`.
357 private fun mproject_node
(mproject
: MProject): NeoNode do
358 var node
= make_node
(mproject
)
359 node
.labels
.add
"MProject"
360 var root
= mproject
.root
362 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
367 # Build a new `MProject` from a `node`.
369 # REQUIRE `node.labels.has("MProject")`
370 private fun to_mproject
(model
: Model, node
: NeoNode): MProject do
371 if mentities
.has_key
(node
) then return mentities
[node
].as(MProject)
372 assert node
.labels
.has
("MProject")
373 var mproject
= new MProject(node
["name"].to_s
, model
)
374 mentities
[node
] = mproject
375 set_doc
(node
, mproject
)
376 mproject
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
380 # Build a `NeoNode` representing `mgroup`.
381 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
382 var node
= make_node
(mgroup
)
383 node
.labels
.add
"MGroup"
384 node
["full_name"] = mgroup
.full_name
385 var parent
= mgroup
.parent
386 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mproject
)))
387 if parent
!= null then
388 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
390 for mmodule
in mgroup
.mmodules
do
391 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
393 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
394 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
399 # Build a new `MGroup` from a `node`.
401 # REQUIRE `node.labels.has("MGroup")`
402 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
403 if mentities
.has_key
(node
) then return mentities
[node
].as(MGroup)
404 assert node
.labels
.has
("MGroup")
405 var mproject
= to_mproject
(model
, node
.out_nodes
("PROJECT").first
)
406 var parent
: nullable MGroup = null
407 var out
= node
.out_nodes
("PARENT")
408 if not out
.is_empty
then
409 parent
= to_mgroup
(model
, out
.first
)
411 var mgroup
= new MGroup(node
["name"].to_s
, mproject
, parent
)
412 mentities
[node
] = mgroup
413 set_doc
(node
, mgroup
)
417 # Build a `NeoNode` representing `mmodule`.
418 private fun mmodule_node
(mmodule
: MModule): NeoNode do
419 var node
= make_node
(mmodule
)
420 node
.labels
.add
"MModule"
421 node
["full_name"] = mmodule
.full_name
422 node
["location"] = mmodule
.location
.to_s
423 var mgroup
= mmodule
.mgroup
424 for parent
in mmodule
.in_importation
.direct_greaters
do
425 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
427 for mclass
in mmodule
.intro_mclasses
do
428 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
430 for mclassdef
in mmodule
.mclassdefs
do
431 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
436 # Build a new `MModule` from a `node`.
438 # REQUIRE `node.labels.has("MModule")`
439 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
440 if mentities
.has_key
(node
) then return mentities
[node
].as(MModule)
441 assert node
.labels
.has
("MModule")
442 var ins
= node
.in_nodes
("DECLARES")
443 var mgroup
: nullable MGroup = null
444 if not ins
.is_empty
then
445 mgroup
= to_mgroup
(model
, ins
.first
)
447 var name
= node
["name"].to_s
448 var location
= to_location
(node
["location"].to_s
)
449 var mmodule
= new MModule(model
, mgroup
, name
, location
)
450 mentities
[node
] = mmodule
451 set_doc
(node
, mmodule
)
452 var imported_mmodules
= new Array[MModule]
453 for smod
in node
.out_nodes
("IMPORTS") do
454 imported_mmodules
.add to_mmodule
(model
, smod
)
456 mmodule
.set_imported_mmodules
(imported_mmodules
)
460 # Build a `NeoNode` representing `mclass`.
461 private fun mclass_node
(mclass
: MClass): NeoNode do
462 var node
= make_node
(mclass
)
463 node
.labels
.add
"MClass"
464 node
["arity"] = mclass
.arity
465 node
["full_name"] = mclass
.full_name
466 node
["kind"] = mclass
.kind
.to_s
467 node
["visibility"] = mclass
.visibility
.to_s
468 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
472 # Build a new `MClass` from a `node`.
474 # REQUIRE `node.labels.has("MClass")`
475 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
476 if mentities
.has_key
(node
) then return mentities
[node
].as(MClass)
477 assert node
.labels
.has
("MClass")
478 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
479 var name
= node
["name"].to_s
480 var arity
= node
["arity"].to_s
.to_i
481 var kind
= to_kind
(node
["kind"].to_s
)
482 var visibility
= to_visibility
(node
["visibility"].to_s
)
483 var mclass
= new MClass(mmodule
, name
, arity
, kind
, visibility
)
484 mentities
[node
] = mclass
485 set_doc
(node
, mclass
)
489 # Build a `NeoNode` representing `mclassdef`.
490 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
491 var node
= make_node
(mclassdef
)
492 node
.labels
.add
"MClassDef"
493 node
["is_intro"] = mclassdef
.is_intro
494 node
["location"] = mclassdef
.location
.to_s
495 if not mclassdef
.parameter_names
.is_empty
then
496 node
["parameter_names"] = new JsonArray.from
(mclassdef
.parameter_names
)
498 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
499 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
500 for mproperty
in mclassdef
.intro_mproperties
do
501 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
503 for mpropdef
in mclassdef
.mpropdefs
do
504 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
506 for sup
in mclassdef
.supertypes
do
507 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
512 # Build a new `MClassDef` from a `node`.
514 # REQUIRE `node.labels.has("MClassDef")`
515 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
516 if mentities
.has_key
(node
) then return mentities
[node
].as(MClassDef)
517 assert node
.labels
.has
("MClassDef")
518 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
519 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
520 var location
= to_location
(node
["location"].to_s
)
521 var parameter_names
= new Array[String]
522 if node
.has_key
("parameter_names") then
523 for e
in node
["parameter_names"].as(JsonArray) do
524 parameter_names
.add e
.to_s
527 var mclassdef
= new MClassDef(mmodule
, mtype
, location
, parameter_names
)
528 mentities
[node
] = mclassdef
529 set_doc
(node
, mclassdef
)
530 var supertypes
= new Array[MClassType]
531 for sup
in node
.out_nodes
("INHERITS") do
532 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
534 mclassdef
.set_supertypes
(supertypes
)
535 mclassdef
.add_in_hierarchy
539 # Build a `NeoNode` representing `mproperty`.
540 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
541 var node
= make_node
(mproperty
)
542 node
.labels
.add
"MProperty"
543 node
["full_name"] = mproperty
.full_name
544 node
["visibility"] = mproperty
.visibility
.to_s
545 if mproperty
isa MMethod then
546 node
.labels
.add
"MMethod"
547 node
["is_init"] = mproperty
.is_init
548 else if mproperty
isa MAttribute then
549 node
.labels
.add
"MAttribute"
550 else if mproperty
isa MVirtualTypeProp then
551 node
.labels
.add
"MVirtualTypeProp"
553 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
557 # Build a new `MProperty` from a `node`.
559 # REQUIRE `node.labels.has("MProperty")`
560 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
561 if mentities
.has_key
(node
) then return mentities
[node
].as(MProperty)
562 assert node
.labels
.has
("MProperty")
563 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
564 var name
= node
["name"].to_s
565 var visibility
= to_visibility
(node
["visibility"].to_s
)
566 var mprop
: nullable MProperty = null
567 if node
.labels
.has
("MMethod") then
568 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
569 mprop
.is_init
= node
["is_init"].as(Bool)
570 else if node
.labels
.has
("MAttribute") then
571 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
572 else if node
.labels
.has
("MVirtualTypeProp") then
573 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
575 if mprop
== null then
576 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
579 mentities
[node
] = mprop
581 for npropdef
in node
.in_nodes
("DEFINES") do
582 var mpropdef
= to_mpropdef
(model
, npropdef
)
583 if npropdef
["is_intro"].as(Bool) then
584 mprop
.mpropdefs
.unshift mpropdef
586 mprop
.mpropdefs
.add mpropdef
592 # Build a `NeoNode` representing `mpropdef`.
593 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
594 var node
= make_node
(mpropdef
)
595 node
.labels
.add
"MPropDef"
596 node
["is_intro"] = mpropdef
.is_intro
597 node
["location"] = mpropdef
.location
.to_s
598 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
599 if mpropdef
isa MMethodDef then
600 node
.labels
.add
"MMethodDef"
601 node
["is_abstract"] = mpropdef
.is_abstract
602 node
["is_intern"] = mpropdef
.is_intern
603 node
["is_extern"] = mpropdef
.is_extern
604 var msignature
= mpropdef
.msignature
605 if msignature
!= null then
606 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
608 else if mpropdef
isa MAttributeDef then
609 node
.labels
.add
"MAttributeDef"
610 else if mpropdef
isa MVirtualTypeDef then
611 node
.labels
.add
"MVirtualTypeDef"
612 var bound
= mpropdef
.bound
613 if bound
!= null then
614 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
620 # Build a new `MPropDef` from a `node`.
622 # REQUIRE `node.labels.has("MPropDef")`
623 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
624 if mentities
.has_key
(node
) then return mentities
[node
].as(MPropDef)
625 assert node
.labels
.has
("MPropDef")
626 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
627 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
628 var location
= to_location
(node
["location"].to_s
)
629 var mpropdef
: nullable MPropDef = null
630 if node
.labels
.has
("MMethodDef") then
631 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
632 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
633 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
634 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
635 mentities
[node
] = mpropdef
636 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
637 else if node
.labels
.has
("MAttributeDef") then
638 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
639 mentities
[node
] = mpropdef
640 else if node
.labels
.has
("MVirtualTypeDef") then
641 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
642 mentities
[node
] = mpropdef
643 var bound
= node
.out_nodes
("BOUND")
644 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
646 if mpropdef
== null then
647 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
650 set_doc
(node
, mpropdef
)
654 # Build a `NeoNode` representing `mtype`.
655 private fun mtype_node
(mtype
: MType): NeoNode do
656 var node
= make_node
(mtype
)
657 node
.labels
.add
"MType"
658 if mtype
isa MClassType then
659 node
.labels
.add
"MClassType"
660 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
661 for arg
in mtype
.arguments
do
662 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
664 if mtype
isa MGenericType then
665 node
.labels
.add
"MGenericType"
667 else if mtype
isa MVirtualType then
668 node
.labels
.add
"MVirtualType"
669 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
670 else if mtype
isa MParameterType then
671 node
.labels
.add
"MParameterType"
672 node
["rank"] = mtype
.rank
673 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
674 else if mtype
isa MNullableType then
675 node
.labels
.add
"MNullableType"
676 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
677 else if mtype
isa MSignature then
678 node
.labels
.add
"MSignature"
679 var names
= new JsonArray
681 for mparameter
in mtype
.mparameters
do
682 names
.add mparameter
.name
683 var pnode
= to_node
(mparameter
)
685 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
687 if not names
.is_empty
then node
["parameter_names"] = names
688 var return_mtype
= mtype
.return_mtype
689 if return_mtype
!= null then
690 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
696 # Build a new `MType` from a `node`.
698 # REQUIRE `node.labels.has("MType")`
699 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
700 if mentities
.has_key
(node
) then return mentities
[node
].as(MType)
701 assert node
.labels
.has
("MType")
702 if node
.labels
.has
("MClassType") then
703 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
704 var args
= new Array[MType]
705 for narg
in node
.out_nodes
("ARGUMENT") do
706 args
.add to_mtype
(model
, narg
)
708 var mtype
= mclass
.get_mtype
(args
)
709 mentities
[node
] = mtype
711 else if node
.labels
.has
("MParameterType") then
712 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
713 var rank
= node
["rank"].to_s
.to_i
714 var mtype
= new MParameterType(mclass
, rank
)
715 mentities
[node
] = mtype
717 else if node
.labels
.has
("MNullableType") then
718 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
719 var mtype
= new MNullableType(intype
)
720 mentities
[node
] = mtype
722 else if node
.labels
.has
("MVirtualType") then
723 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
724 var mtype
= new MVirtualType(mproperty
)
725 mentities
[node
] = mtype
727 else if node
.labels
.has
("MSignature") then
728 # Get all param nodes
729 var mparam_nodes
= new HashMap[String, MParameter]
730 for pnode
in node
.out_nodes
("PARAMETER") do
731 var mparam
= to_mparameter
(model
, pnode
)
732 mparam_nodes
[mparam
.name
] = mparam
734 # Load params in the good order
735 var mparam_names
= node
["parameter_names"]
736 var mparameters
= new Array[MParameter]
737 if mparam_names
isa JsonArray then
738 for mparam_name
in mparam_names
do
739 var mparam
= mparam_nodes
[mparam_name
.to_s
]
740 mparameters
.add mparam
743 var return_mtype
: nullable MType = null
744 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
745 if not ret_nodes
.is_empty
then
746 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
748 var mtype
= new MSignature(mparameters
, return_mtype
)
749 mentities
[node
] = mtype
752 print
"not yet implemented to_mtype for {node.labels.join(",")}"
756 # Build a `NeoNode` representing `mparameter`.
757 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
758 var node
= make_node
(mparameter
)
759 node
.labels
.add
"MParameter"
760 node
["name"] = mparameter
.name
761 node
["is_vararg"] = mparameter
.is_vararg
762 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
766 # Build a new `MParameter` from `node`.
768 # REQUIRE `node.labels.has("MParameter")`
769 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
770 if mentities
.has_key
(node
) then return mentities
[node
].as(MParameter)
771 assert node
.labels
.has
("MParameter")
772 var name
= node
["name"].to_s
773 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
774 var is_vararg
= node
["is_vararg"].as(Bool)
775 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
776 mentities
[node
] = mparameter
780 # Get a `Location` from its string representation.
781 private fun to_location
(loc
: String): Location do
783 var parts
= loc
.split_with
(":")
784 var file
= new SourceFile.from_string
(parts
[0], "")
785 var pos
= parts
[1].split_with
("--")
786 var pos1
= pos
[0].split_with
(",")
787 var pos2
= pos
[1].split_with
(",")
788 var line_s
= pos1
[0].to_i
789 var line_e
= pos2
[0].to_i
790 var column_s
= pos1
[1].to_i
792 if pos2
.length
== 2 then pos2
[1].to_i
793 return new Location(file
, line_s
, line_e
, column_s
, column_e
)
796 # Get a `MVisibility` from its string representation.
797 private fun to_visibility
(vis
: String): MVisibility do
798 if vis
== intrude_visibility
.to_s
then
799 return intrude_visibility
800 else if vis
== public_visibility
.to_s
then
801 return public_visibility
802 else if vis
== protected_visibility
.to_s
then
803 return protected_visibility
804 else if vis
== private_visibility
.to_s
then
805 return private_visibility
807 return none_visibility
811 # Get a `MKind` from its string representation.
812 private fun to_kind
(kind
: String): MClassKind do
813 if kind
== abstract_kind
.to_s
then
815 else if kind
== concrete_kind
.to_s
then
817 else if kind
== interface_kind
.to_s
then
818 return interface_kind
819 else if kind
== enum_kind
.to_s
then
821 else if kind
== extern_kind
.to_s
then
827 # Extract the `MDoc` from `node` and link it to `mentity`.
828 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
829 if node
.has_key
("mdoc") then
830 var lines
= new Array[String]
831 for e
in node
["mdoc"].as(JsonArray) do
832 lines
.add e
.to_s
#.replace("\n", "\\n")
835 mdoc
.content
.add_all
(lines
)
836 mdoc
.original_mentity
= mentity