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:
28 # * labels: `model_name`, `MEntity`, `MProject`
29 # * `(:MProject)-[:ROOT]->(:MGroup)`
33 # * labels: `model_name`, `MEntity`, `MGroup`
34 # * `(:MGroup)-[:PROJECT]->(:MProject)`
35 # * `(:MGroup)-[:PARENT]->(:MGroup)`
39 # * labels: `model_name`, `MEntity`, `MModule`
40 # * `(:MModule)-[:IMPORTS]->(:MModule)`
41 # * `(:MModule)-[:INTRODUCES]->(:MClass)`
42 # * `(:MModule)-[:DEFINES]->(:MClassDef)`
46 # * labels: `model_name`, `MEntity`, `MClass`
47 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`
51 # * labels: `model_name`, `MEntity`, `MClassDef`
52 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`
53 # * `(:MClassDef)-[:MCLASS]->(:MClass)`
54 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`
55 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`
59 # * labels: `model_name`, `MEntity`, `MProperty`
60 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`
62 # MProperties can also have labels `MMethod`, `MAttribute`, `MVirtualTypeProp`.
66 # * labels: `model_name`, `MEntity`, `MPropDef`
67 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`
69 # MPropdefs can also have labels `MMethodDef`, `MAttributeDef`, `MVirtualTypeDef`.
71 # `MMethodDef` are linked to a `MSignature`:
73 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`
75 # `MVirtualTypeDef` are linked to a `MType` (its `bound`):
77 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`
81 # * labels: `model_name`, `MEntity`, `MType`
83 # MTypes can also have labels `MClassType`, `MGenericType`, `MNullableType`, `MVirtualType`
86 # `MClassType` and `MGenericType` both point to a `MClass` and have type `arguments`:
88 # * `(:MClassType)-[:CLASS]->(:MClass)`
89 # * `(:MClassType)-[:ARGUMENT]->(:MType)`
91 # `MVirtualType` points to its introducing `MProperty`:
93 # * `(:MVirtualType)-[:PROPERTY]->(:MVirtualTypeDef)`
95 # `MParameterType` points to its introducing `MClass`:
97 # * `(:MParameterType)-[:CLASS]->(:MClass)`
99 # `MNullableType` points to its non-nullable `MType`:
101 # * `(:MNullableType)-[:TYPE]->(:MType)`
103 # `MSignature` can have `parameters` and a `return_mtype`:
105 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`
106 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`
108 # In order to maintain the correct parameters order, each `MSignature` node contains
109 # an array of parameter names corresponding to the parameter order in the signature.
111 # For example, if the source code contains:
113 # fun foo(a: A, b: B, c: C)
115 # The `MSignature` node will contain a property `parameter_names = ["a", "b", "c"]` so
116 # the MSignature can be reconstructed with the parameters in the correct order.
120 # * labels: `model_name`, `MEntity`, `MParameter`
121 # * `(:MParameter)-[:TYPE]->(:MType)`
123 # MParameters are also ranked by their position in the corresponding signature.
124 # Rank 0 for the first parameter, 1 for the next one and etc.
131 # Helper class that can save and load a `Model` into a Neo4j database.
136 # Because we use only one Neo4j instance to store all the models,
137 # we need to mark their appartenance to a particular model and avoid loading all models.
139 # The name is used as a Neo label on each created nodes and used to load nodes from base.
140 var model_name
: String
142 # The toolcontext used to init the `NeoModel` tool.
143 var toolcontext
: ToolContext
145 # The Neo4j `client` used to communicate with the Neo4j instance.
146 var client
: Neo4jClient
148 # Fill `model` using base pointed by `client`.
149 fun load
(model
: Model): Model do
150 toolcontext
.info
("Locate all mentities...", 1)
151 var nodes
= client
.nodes_with_label
(model_name
)
153 toolcontext
.info
("Preload nodes...", 1)
154 pull_all_nodes
(nodes
)
155 toolcontext
.info
("Preload edges...", 1)
156 pull_all_edges
(nodes
)
158 toolcontext
.info
("Build model...", 1)
159 nodes
= client
.nodes_with_labels
([model_name
, "MProject"])
160 for node
in nodes
do to_mproject
(model
, node
)
161 nodes
= client
.nodes_with_labels
([model_name
, "MGroup"])
162 for node
in nodes
do to_mgroup
(model
, node
)
163 nodes
= client
.nodes_with_labels
([model_name
, "MModule"])
164 for node
in nodes
do to_mmodule
(model
, node
)
165 nodes
= client
.nodes_with_labels
([model_name
, "MClass"])
166 for node
in nodes
do to_mclass
(model
, node
)
167 nodes
= client
.nodes_with_labels
([model_name
, "MClassDef"])
168 for node
in nodes
do to_mclassdef
(model
, node
)
169 nodes
= client
.nodes_with_labels
([model_name
, "MProperty"])
170 for node
in nodes
do to_mproperty
(model
, node
)
171 nodes
= client
.nodes_with_labels
([model_name
, "MPropDef"])
172 for node
in nodes
do to_mpropdef
(model
, node
)
176 # Save `model` in the base pointed by `client`.
177 fun save
(model
: Model) do
178 var nodes
= collect_model_nodes
(model
)
179 toolcontext
.info
("Save {nodes.length} nodes...", 1)
181 var edges
= collect_model_edges
(model
)
182 toolcontext
.info
("Save {edges.length} edges...", 1)
186 # Save `neo_entities` in base using batch mode.
187 private fun push_all
(neo_entities
: Collection[NeoEntity]) do
188 var batch
= new NeoBatch(client
)
189 var len
= neo_entities
.length
192 for nentity
in neo_entities
do
193 batch
.save_entity
(nentity
)
194 if i
== batch_max_size
then
196 sum
+= batch_max_size
197 toolcontext
.info
(" {sum * 100 / len}% done", 1)
198 batch
= new NeoBatch(client
)
207 # Load content for all `nodes` from base.
209 # Content corresponds to properties and labels that are loaded in batch mode.
210 private fun pull_all_nodes
(nodes
: Collection[NeoNode]) do
211 var batch
= new NeoBatch(client
)
212 var len
= nodes
.length
216 batch
.load_node
(node
)
217 if i
== batch_max_size
then
219 sum
+= batch_max_size
220 toolcontext
.info
(" {sum * 100 / len}% done", 1)
221 batch
= new NeoBatch(client
)
230 # Load all edges from base linked to `nodes`.
232 # Edges are loaded in batch mode.
233 private fun pull_all_edges
(nodes
: Collection[NeoNode]) do
234 var batch
= new NeoBatch(client
)
235 var len
= nodes
.length
239 batch
.load_node_edges
(node
)
240 if i
== batch_max_size
then
242 sum
+= batch_max_size
243 toolcontext
.info
(" {sum * 100 / len}% done", 1)
244 batch
= new NeoBatch(client
)
253 # How many operation can be executed in one batch?
254 private var batch_max_size
= 1000
256 # Execute `batch` and check for errors.
258 # Abort if `batch.execute` returns errors.
259 private fun do_batch
(batch
: NeoBatch) do
260 var errors
= batch
.execute
261 if not errors
.is_empty
then
267 # Collect all nodes from the current `model`.
268 private fun collect_model_nodes
(model
: Model): Collection[NeoNode] do
269 for mproject
in model
.mprojects
do
271 for mgroup
in mproject
.mgroups
do to_node
(mgroup
)
276 # Collect all edges from the current `model`.
278 # Actually collect all out_edges from all nodes.
279 private fun collect_model_edges
(model
: Model): Collection[NeoEdge] do
280 var edges
= new HashSet[NeoEdge]
281 for node
in nodes
.values
do edges
.add_all
(node
.out_edges
)
285 # Mentities associated to nodes.
286 private var mentities
= new HashMap[NeoNode, MEntity]
288 # Nodes associated with MEntities.
289 private var nodes
= new HashMap[MEntity, NeoNode]
291 # Get the `NeoNode` associated with `mentity`.
292 # `mentities` are stored locally to avoid duplication.
293 fun to_node
(mentity
: MEntity): NeoNode do
294 if nodes
.has_key
(mentity
) then return nodes
[mentity
]
295 if mentity
isa MProject then return mproject_node
(mentity
)
296 if mentity
isa MGroup then return mgroup_node
(mentity
)
297 if mentity
isa MModule then return mmodule_node
(mentity
)
298 if mentity
isa MClass then return mclass_node
(mentity
)
299 if mentity
isa MClassDef then return mclassdef_node
(mentity
)
300 if mentity
isa MProperty then return mproperty_node
(mentity
)
301 if mentity
isa MPropDef then return mpropdef_node
(mentity
)
302 if mentity
isa MType then return mtype_node
(mentity
)
303 if mentity
isa MParameter then return mparameter_node
(mentity
)
307 # Make a new `NeoNode` based on `mentity`.
308 private fun make_node
(mentity
: MEntity): NeoNode do
309 var node
= new NeoNode
310 nodes
[mentity
] = node
311 node
.labels
.add
"MEntity"
312 node
.labels
.add model_name
313 node
["name"] = mentity
.name
314 if mentity
.mdoc
!= null then node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
318 # Build a `NeoNode` representing `mproject`.
319 private fun mproject_node
(mproject
: MProject): NeoNode do
320 var node
= make_node
(mproject
)
321 node
.labels
.add
"MProject"
322 var root
= mproject
.root
324 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
329 # Build a new `MProject` from a `node`.
331 # REQUIRE `node.labels.has("MProject")`
332 private fun to_mproject
(model
: Model, node
: NeoNode): MProject do
333 if mentities
.has_key
(node
) then return mentities
[node
].as(MProject)
334 assert node
.labels
.has
("MProject")
335 var mproject
= new MProject(node
["name"].to_s
, model
)
336 mentities
[node
] = mproject
337 set_doc
(node
, mproject
)
338 mproject
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
342 # Build a `NeoNode` representing `mgroup`.
343 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
344 var node
= make_node
(mgroup
)
345 node
.labels
.add
"MGroup"
346 node
["full_name"] = mgroup
.full_name
347 var parent
= mgroup
.parent
348 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mproject
)))
349 if parent
!= null then
350 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
352 for mmodule
in mgroup
.mmodules
do
353 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
355 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
356 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
361 # Build a new `MGroup` from a `node`.
363 # REQUIRE `node.labels.has("MGroup")`
364 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
365 if mentities
.has_key
(node
) then return mentities
[node
].as(MGroup)
366 assert node
.labels
.has
("MGroup")
367 var mproject
= to_mproject
(model
, node
.out_nodes
("PROJECT").first
)
368 var parent
: nullable MGroup = null
369 var out
= node
.out_nodes
("PARENT")
370 if not out
.is_empty
then
371 parent
= to_mgroup
(model
, out
.first
)
373 var mgroup
= new MGroup(node
["name"].to_s
, mproject
, parent
)
374 mentities
[node
] = mgroup
375 set_doc
(node
, mgroup
)
379 # Build a `NeoNode` representing `mmodule`.
380 private fun mmodule_node
(mmodule
: MModule): NeoNode do
381 var node
= make_node
(mmodule
)
382 node
.labels
.add
"MModule"
383 node
["full_name"] = mmodule
.full_name
384 node
["location"] = mmodule
.location
.to_s
385 var mgroup
= mmodule
.mgroup
386 for parent
in mmodule
.in_importation
.direct_greaters
do
387 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
389 for mclass
in mmodule
.intro_mclasses
do
390 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
392 for mclassdef
in mmodule
.mclassdefs
do
393 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
398 # Build a new `MModule` from a `node`.
400 # REQUIRE `node.labels.has("MModule")`
401 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
402 if mentities
.has_key
(node
) then return mentities
[node
].as(MModule)
403 assert node
.labels
.has
("MModule")
404 var ins
= node
.in_nodes
("DECLARES")
405 var mgroup
: nullable MGroup = null
406 if not ins
.is_empty
then
407 mgroup
= to_mgroup
(model
, ins
.first
)
409 var name
= node
["name"].to_s
410 var location
= to_location
(node
["location"].to_s
)
411 var mmodule
= new MModule(model
, mgroup
, name
, location
)
412 mentities
[node
] = mmodule
413 set_doc
(node
, mmodule
)
414 var imported_mmodules
= new Array[MModule]
415 for smod
in node
.out_nodes
("IMPORTS") do
416 imported_mmodules
.add to_mmodule
(model
, smod
)
418 mmodule
.set_imported_mmodules
(imported_mmodules
)
422 # Build a `NeoNode` representing `mclass`.
423 private fun mclass_node
(mclass
: MClass): NeoNode do
424 var node
= make_node
(mclass
)
425 node
.labels
.add
"MClass"
426 node
["arity"] = mclass
.arity
427 node
["full_name"] = mclass
.full_name
428 node
["kind"] = mclass
.kind
.to_s
429 node
["visibility"] = mclass
.visibility
.to_s
430 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
434 # Build a new `MClass` from a `node`.
436 # REQUIRE `node.labels.has("MClass")`
437 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
438 if mentities
.has_key
(node
) then return mentities
[node
].as(MClass)
439 assert node
.labels
.has
("MClass")
440 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
441 var name
= node
["name"].to_s
442 var arity
= node
["arity"].to_s
.to_i
443 var kind
= to_kind
(node
["kind"].to_s
)
444 var visibility
= to_visibility
(node
["visibility"].to_s
)
445 var mclass
= new MClass(mmodule
, name
, arity
, kind
, visibility
)
446 mentities
[node
] = mclass
447 set_doc
(node
, mclass
)
451 # Build a `NeoNode` representing `mclassdef`.
452 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
453 var node
= make_node
(mclassdef
)
454 node
.labels
.add
"MClassDef"
455 node
["is_intro"] = mclassdef
.is_intro
456 node
["location"] = mclassdef
.location
.to_s
457 if not mclassdef
.parameter_names
.is_empty
then
458 node
["parameter_names"] = new JsonArray.from
(mclassdef
.parameter_names
)
460 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
461 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
462 for mproperty
in mclassdef
.intro_mproperties
do
463 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
465 for mpropdef
in mclassdef
.mpropdefs
do
466 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
468 for sup
in mclassdef
.supertypes
do
469 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
474 # Build a new `MClassDef` from a `node`.
476 # REQUIRE `node.labels.has("MClassDef")`
477 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
478 if mentities
.has_key
(node
) then return mentities
[node
].as(MClassDef)
479 assert node
.labels
.has
("MClassDef")
480 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
481 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
482 var location
= to_location
(node
["location"].to_s
)
483 var parameter_names
= new Array[String]
484 if node
.has_key
("parameter_names") then
485 for e
in node
["parameter_names"].as(JsonArray) do
486 parameter_names
.add e
.to_s
489 var mclassdef
= new MClassDef(mmodule
, mtype
, location
, parameter_names
)
490 mentities
[node
] = mclassdef
491 set_doc
(node
, mclassdef
)
492 var supertypes
= new Array[MClassType]
493 for sup
in node
.out_nodes
("INHERITS") do
494 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
496 mclassdef
.set_supertypes
(supertypes
)
497 mclassdef
.add_in_hierarchy
501 # Build a `NeoNode` representing `mproperty`.
502 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
503 var node
= make_node
(mproperty
)
504 node
.labels
.add
"MProperty"
505 node
["full_name"] = mproperty
.full_name
506 node
["visibility"] = mproperty
.visibility
.to_s
507 if mproperty
isa MMethod then
508 node
.labels
.add
"MMethod"
509 node
["is_init"] = mproperty
.is_init
510 else if mproperty
isa MAttribute then
511 node
.labels
.add
"MAttribute"
512 else if mproperty
isa MVirtualTypeProp then
513 node
.labels
.add
"MVirtualTypeProp"
515 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
519 # Build a new `MProperty` from a `node`.
521 # REQUIRE `node.labels.has("MProperty")`
522 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
523 if mentities
.has_key
(node
) then return mentities
[node
].as(MProperty)
524 assert node
.labels
.has
("MProperty")
525 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
526 var name
= node
["name"].to_s
527 var visibility
= to_visibility
(node
["visibility"].to_s
)
528 var mprop
: nullable MProperty = null
529 if node
.labels
.has
("MMethod") then
530 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
531 mprop
.is_init
= node
["is_init"].as(Bool)
532 else if node
.labels
.has
("MAttribute") then
533 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
534 else if node
.labels
.has
("MVirtualTypeProp") then
535 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
537 if mprop
== null then
538 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
541 mentities
[node
] = mprop
543 for npropdef
in node
.in_nodes
("DEFINES") do
544 var mpropdef
= to_mpropdef
(model
, npropdef
)
545 if npropdef
["is_intro"].as(Bool) then
546 mprop
.mpropdefs
.unshift mpropdef
548 mprop
.mpropdefs
.add mpropdef
554 # Build a `NeoNode` representing `mpropdef`.
555 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
556 var node
= make_node
(mpropdef
)
557 node
.labels
.add
"MPropDef"
558 node
["is_intro"] = mpropdef
.is_intro
559 node
["location"] = mpropdef
.location
.to_s
560 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
561 if mpropdef
isa MMethodDef then
562 node
.labels
.add
"MMethodDef"
563 node
["is_abstract"] = mpropdef
.is_abstract
564 node
["is_intern"] = mpropdef
.is_intern
565 node
["is_extern"] = mpropdef
.is_extern
566 var msignature
= mpropdef
.msignature
567 if msignature
!= null then
568 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
570 else if mpropdef
isa MAttributeDef then
571 node
.labels
.add
"MAttributeDef"
572 else if mpropdef
isa MVirtualTypeDef then
573 node
.labels
.add
"MVirtualTypeDef"
574 var bound
= mpropdef
.bound
575 if bound
!= null then
576 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
582 # Build a new `MPropDef` from a `node`.
584 # REQUIRE `node.labels.has("MPropDef")`
585 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
586 if mentities
.has_key
(node
) then return mentities
[node
].as(MPropDef)
587 assert node
.labels
.has
("MPropDef")
588 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
589 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
590 var location
= to_location
(node
["location"].to_s
)
591 var mpropdef
: nullable MPropDef = null
592 if node
.labels
.has
("MMethodDef") then
593 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
594 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
595 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
596 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
597 mentities
[node
] = mpropdef
598 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
599 else if node
.labels
.has
("MAttributeDef") then
600 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
601 mentities
[node
] = mpropdef
602 else if node
.labels
.has
("MVirtualTypeDef") then
603 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
604 mentities
[node
] = mpropdef
605 var bound
= node
.out_nodes
("BOUND")
606 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
608 if mpropdef
== null then
609 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
612 set_doc
(node
, mpropdef
)
616 # Build a `NeoNode` representing `mtype`.
617 private fun mtype_node
(mtype
: MType): NeoNode do
618 var node
= make_node
(mtype
)
619 node
.labels
.add
"MType"
620 if mtype
isa MClassType then
621 node
.labels
.add
"MClassType"
622 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
623 for arg
in mtype
.arguments
do
624 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
626 if mtype
isa MGenericType then
627 node
.labels
.add
"MGenericType"
629 else if mtype
isa MVirtualType then
630 node
.labels
.add
"MVirtualType"
631 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
632 else if mtype
isa MParameterType then
633 node
.labels
.add
"MParameterType"
634 node
["rank"] = mtype
.rank
635 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
636 else if mtype
isa MNullableType then
637 node
.labels
.add
"MNullableType"
638 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
639 else if mtype
isa MSignature then
640 node
.labels
.add
"MSignature"
641 var names
= new JsonArray
643 for mparameter
in mtype
.mparameters
do
644 names
.add mparameter
.name
645 var pnode
= to_node
(mparameter
)
647 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
649 if not names
.is_empty
then node
["parameter_names"] = names
650 var return_mtype
= mtype
.return_mtype
651 if return_mtype
!= null then
652 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
658 # Build a new `MType` from a `node`.
660 # REQUIRE `node.labels.has("MType")`
661 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
662 if mentities
.has_key
(node
) then return mentities
[node
].as(MType)
663 assert node
.labels
.has
("MType")
664 if node
.labels
.has
("MClassType") then
665 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
666 var args
= new Array[MType]
667 for narg
in node
.out_nodes
("ARGUMENT") do
668 args
.add to_mtype
(model
, narg
)
670 var mtype
= mclass
.get_mtype
(args
)
671 mentities
[node
] = mtype
673 else if node
.labels
.has
("MParameterType") then
674 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
675 var rank
= node
["rank"].to_s
.to_i
676 var mtype
= new MParameterType(mclass
, rank
)
677 mentities
[node
] = mtype
679 else if node
.labels
.has
("MNullableType") then
680 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
681 var mtype
= new MNullableType(intype
)
682 mentities
[node
] = mtype
684 else if node
.labels
.has
("MVirtualType") then
685 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
686 var mtype
= new MVirtualType(mproperty
)
687 mentities
[node
] = mtype
689 else if node
.labels
.has
("MSignature") then
690 # Get all param nodes
691 var mparam_nodes
= new HashMap[String, MParameter]
692 for pnode
in node
.out_nodes
("PARAMETER") do
693 var mparam
= to_mparameter
(model
, pnode
)
694 mparam_nodes
[mparam
.name
] = mparam
696 # Load params in the good order
697 var mparam_names
= node
["parameter_names"]
698 var mparameters
= new Array[MParameter]
699 if mparam_names
isa JsonArray then
700 for mparam_name
in mparam_names
do
701 var mparam
= mparam_nodes
[mparam_name
.to_s
]
702 mparameters
.add mparam
705 var return_mtype
: nullable MType = null
706 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
707 if not ret_nodes
.is_empty
then
708 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
710 var mtype
= new MSignature(mparameters
, return_mtype
)
711 mentities
[node
] = mtype
714 print
"not yet implemented to_mtype for {node.labels.join(",")}"
718 # Build a `NeoNode` representing `mparameter`.
719 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
720 var node
= make_node
(mparameter
)
721 node
.labels
.add
"MParameter"
722 node
["name"] = mparameter
.name
723 node
["is_vararg"] = mparameter
.is_vararg
724 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
728 # Build a new `MParameter` from `node`.
730 # REQUIRE `node.labels.has("MParameter")`
731 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
732 if mentities
.has_key
(node
) then return mentities
[node
].as(MParameter)
733 assert node
.labels
.has
("MParameter")
734 var name
= node
["name"].to_s
735 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
736 var is_vararg
= node
["is_vararg"].as(Bool)
737 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
738 mentities
[node
] = mparameter
742 # Get a `Location` from its string representation.
743 private fun to_location
(loc
: String): Location do
745 var parts
= loc
.split_with
(":")
746 var file
= new SourceFile.from_string
(parts
[0], "")
747 var pos
= parts
[1].split_with
("--")
748 var pos1
= pos
[0].split_with
(",")
749 var pos2
= pos
[1].split_with
(",")
750 var line_s
= pos1
[0].to_i
751 var line_e
= pos2
[0].to_i
752 var column_s
= pos1
[1].to_i
754 if pos2
.length
== 2 then pos2
[1].to_i
755 return new Location(file
, line_s
, line_e
, column_s
, column_e
)
758 # Get a `MVisibility` from its string representation.
759 private fun to_visibility
(vis
: String): MVisibility do
760 if vis
== intrude_visibility
.to_s
then
761 return intrude_visibility
762 else if vis
== public_visibility
.to_s
then
763 return public_visibility
764 else if vis
== protected_visibility
.to_s
then
765 return protected_visibility
766 else if vis
== private_visibility
.to_s
then
767 return private_visibility
769 return none_visibility
773 # Get a `MKind` from its string representation.
774 private fun to_kind
(kind
: String): MClassKind do
775 if kind
== abstract_kind
.to_s
then
777 else if kind
== concrete_kind
.to_s
then
779 else if kind
== interface_kind
.to_s
then
780 return interface_kind
781 else if kind
== enum_kind
.to_s
then
783 else if kind
== extern_kind
.to_s
then
789 # Extract the `MDoc` from `node` and link it to `mentity`.
790 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
791 if node
.has_key
("mdoc") then
792 var lines
= new Array[String]
793 for e
in node
["mdoc"].as(JsonArray) do
794 lines
.add e
.to_s
#.replace("\n", "\\n")
797 mdoc
.content
.add_all
(lines
)
798 mdoc
.original_mentity
= mentity