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 a `Model` to/from a Neo4j graph.
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.
23 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
25 # Structure of the nit `Model` in the graph:
27 # Note : Any null or empty attribute will not be saved in the database.
29 # For any `MEntity` (in addition to specific data):
31 # * labels: model name (`model_name`) and `MEntity`.
32 # * `name`: short (unqualified) name.
33 # * `mdoc`: JSON array representing the associated Markdown documentation
36 # Note : All nodes described here are MEntities.
40 # * labels: `MPackage`, `model_name` and `MEntity`.
41 # * `(:MPackage)-[:ROOT]->(:MGroup)`: root of the group tree.
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
49 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
51 # * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
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
61 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
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`
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`.
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
84 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
85 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
87 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
88 # classdef (introductions and redefinitions).
89 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
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
102 # Additional relationship for `MInnerClass`:
104 # * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
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.
114 # Additional attributes and relationship for `MMethodDef`:
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.
122 # Additional relationship for `MAttributeDef`:
124 # * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
127 # Additional relationship for `MVirtualTypeDef`:
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.
133 # Additional relationship for `MInnerClassDef`:
135 # * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
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.
144 # Additional label and relationships for `MClassType`:
146 # * If it is a `MGenericType`, also has the `MGenericType` label.
147 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
148 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
150 # Arguments are named following the `parameter_names` attribute of the
151 # `MClass` referred by `CLASS`.
153 # Additional relationship for `MVirtualType`:
155 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
156 # determines the type (usually a `MVirtualTypeProp`).
158 # Additional attribute and relationship for `MParameterType`:
160 # * `rank`: position of the parameter (0 for the first parameter).
161 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
164 # Additional relationship for `MNullableType`:
166 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
168 # Additional attribute and relationship for `MRawType`:
170 # * `text`: JSON array of the parts’ text.
171 # * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
173 # Additional attribute and relationships for `MSignature`:
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
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
184 # For example, if the source code contains:
187 # fun foo(a: A, b: B, c: C)
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.
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.
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.
206 # * labels: `MTypePart`, `model_name` and `MEntity`.
207 # * `rank`: position in the `MRawType`.
208 # * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
211 import doc
::model_ext
215 # Helper class that can save and load a `Model` into a Neo4j database.
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.
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
226 # The toolcontext used to init the `NeoModel` tool.
227 var toolcontext
: ToolContext
229 # The Neo4j `client` used to communicate with the Neo4j instance.
230 var client
: Neo4jClient
232 # Fill `model` using base pointed by `client`.
233 fun load
(model
: Model): Model do
234 var nodes
: Array[NeoNode]
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
)
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)
265 var edges
= collect_model_edges
(model
)
266 toolcontext
.info
("Save {edges.length} edges...", 1)
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
276 for nentity
in neo_entities
do
277 batch
.save_entity
(nentity
)
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 mpackage
in model
.mpackages
do
309 for mgroup
in mpackage
.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.
325 # The key is the node’s id.
326 private var mentities
= new HashMap[Int, MEntity]
328 # Nodes associated with MEntities.
329 private var nodes
= new HashMap[MEntity, NeoNode]
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 MPackage then return mpackage_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
)
347 # Get the `MEntity` associated with `node`.
348 fun to_mentity
(model
: Model, node
: NeoNode): MEntity do
349 if node
.labels
.has
("MPackage") then return to_mpackage
(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
)
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 not mentity
isa MSignature then
369 #FIXME: MSignature is a MEntity, but has no model :/
370 node
["location"] = mentity
.location
.to_s
372 var mdoc
= mentity
.mdoc
374 node
["mdoc"] = new JsonArray.from
(mdoc
.content
)
375 node
["mdoc_location"] = mdoc
.location
.to_s
380 # Build a `NeoNode` representing `mpackage`.
381 private fun mpackage_node
(mpackage
: MPackage): NeoNode do
382 var node
= make_node
(mpackage
)
383 node
.labels
.add
"MPackage"
384 var root
= mpackage
.root
386 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
391 # Build a new `MPackage` from a `node`.
393 # REQUIRE `node.labels.has("MPackage")`
394 private fun to_mpackage
(model
: Model, node
: NeoNode): MPackage do
395 var m
= mentities
.get_or_null
(node
.id
.as(Int))
396 if m
isa MPackage then return m
398 assert node
.labels
.has
("MPackage")
399 var location
= to_location
(node
["location"].to_s
)
400 var mpackage
= new MPackage(node
["name"].to_s
, model
, location
)
401 mentities
[node
.id
.as(Int)] = mpackage
402 set_doc
(node
, mpackage
)
403 mpackage
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
407 # Build a `NeoNode` representing `mgroup`.
408 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
409 var node
= make_node
(mgroup
)
410 node
.labels
.add
"MGroup"
411 var parent
= mgroup
.parent
412 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mpackage
)))
413 if parent
!= null then
414 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
416 for mmodule
in mgroup
.mmodules
do
417 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
419 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
420 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
425 # Build a new `MGroup` from a `node`.
427 # REQUIRE `node.labels.has("MGroup")`
428 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
429 var m
= mentities
.get_or_null
(node
.id
.as(Int))
430 if m
isa MGroup then return m
432 assert node
.labels
.has
("MGroup")
433 var location
= to_location
(node
["location"].to_s
)
434 var mpackage
= to_mpackage
(model
, node
.out_nodes
("PROJECT").first
)
435 var parent
: nullable MGroup = null
436 var out
= node
.out_nodes
("PARENT")
437 if not out
.is_empty
then
438 parent
= to_mgroup
(model
, out
.first
)
440 var mgroup
= new MGroup(node
["name"].to_s
, location
, mpackage
, parent
)
441 mentities
[node
.id
.as(Int)] = mgroup
442 set_doc
(node
, mgroup
)
446 # Build a `NeoNode` representing `mmodule`.
447 private fun mmodule_node
(mmodule
: MModule): NeoNode do
448 var node
= make_node
(mmodule
)
449 node
.labels
.add
"MModule"
450 for parent
in mmodule
.in_importation
.direct_greaters
do
451 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
453 for mclass
in mmodule
.intro_mclasses
do
454 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
456 for mclassdef
in mmodule
.mclassdefs
do
457 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
462 # Build a new `MModule` from a `node`.
464 # REQUIRE `node.labels.has("MModule")`
465 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
466 var m
= mentities
.get_or_null
(node
.id
.as(Int))
467 if m
isa MModule then return m
469 assert node
.labels
.has
("MModule")
470 var ins
= node
.in_nodes
("DECLARES")
471 var mgroup
: nullable MGroup = null
472 if not ins
.is_empty
then
473 mgroup
= to_mgroup
(model
, ins
.first
)
475 var name
= node
["name"].to_s
476 var location
= to_location
(node
["location"].to_s
)
477 var mmodule
= new MModule(model
, mgroup
, name
, location
)
478 mentities
[node
.id
.as(Int)] = mmodule
479 set_doc
(node
, mmodule
)
480 var imported_mmodules
= new Array[MModule]
481 for smod
in node
.out_nodes
("IMPORTS") do
482 imported_mmodules
.add to_mmodule
(model
, smod
)
484 mmodule
.set_imported_mmodules
(imported_mmodules
)
488 # Build a `NeoNode` representing `mclass`.
489 private fun mclass_node
(mclass
: MClass): NeoNode do
490 var node
= make_node
(mclass
)
491 node
.labels
.add
"MClass"
492 node
["kind"] = mclass
.kind
.to_s
493 node
["visibility"] = mclass
.visibility
.to_s
494 if not mclass
.mparameters
.is_empty
then
495 var parameter_names
= new Array[String]
496 for p
in mclass
.mparameters
do parameter_names
.add
(p
.name
)
497 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
499 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
503 # Build a new `MClass` from a `node`.
505 # REQUIRE `node.labels.has("MClass")`
506 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
507 var m
= mentities
.get_or_null
(node
.id
.as(Int))
508 if m
isa MClass then return m
510 assert node
.labels
.has
("MClass")
511 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
512 var name
= node
["name"].to_s
513 var location
= to_location
(node
["location"].to_s
)
514 var kind
= to_kind
(node
["kind"].to_s
)
515 var visibility
= to_visibility
(node
["visibility"].to_s
)
516 var parameter_names
= new Array[String]
517 if node
.has_key
("parameter_names") then
518 for e
in node
["parameter_names"].as(JsonArray) do
519 parameter_names
.add e
.to_s
522 var mclass
= new MClass(mmodule
, name
, location
, parameter_names
, kind
, visibility
)
523 mentities
[node
.id
.as(Int)] = mclass
524 set_doc
(node
, mclass
)
528 # Build a `NeoNode` representing `mclassdef`.
529 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
530 var node
= make_node
(mclassdef
)
531 node
.labels
.add
"MClassDef"
532 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
533 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
534 for mproperty
in mclassdef
.intro_mproperties
do
535 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
537 for mpropdef
in mclassdef
.mpropdefs
do
538 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
540 for sup
in mclassdef
.supertypes
do
541 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
546 # Build a new `MClassDef` from a `node`.
548 # REQUIRE `node.labels.has("MClassDef")`
549 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
550 var m
= mentities
.get_or_null
(node
.id
.as(Int))
551 if m
isa MClassDef then return m
553 assert node
.labels
.has
("MClassDef")
554 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
555 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
556 var location
= to_location
(node
["location"].to_s
)
557 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
558 mentities
[node
.id
.as(Int)] = mclassdef
559 set_doc
(node
, mclassdef
)
560 var supertypes
= new Array[MClassType]
561 for sup
in node
.out_nodes
("INHERITS") do
562 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
564 mclassdef
.set_supertypes
(supertypes
)
565 mclassdef
.add_in_hierarchy
569 # Build a `NeoNode` representing `mproperty`.
570 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
571 var node
= make_node
(mproperty
)
572 node
.labels
.add
"MProperty"
573 node
["visibility"] = mproperty
.visibility
.to_s
574 if mproperty
isa MMethod then
575 node
.labels
.add
"MMethod"
576 node
["is_init"] = mproperty
.is_init
577 else if mproperty
isa MAttribute then
578 node
.labels
.add
"MAttribute"
579 else if mproperty
isa MVirtualTypeProp then
580 node
.labels
.add
"MVirtualTypeProp"
581 else if mproperty
isa MInnerClass then
582 node
.labels
.add
"MInnerClass"
583 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mproperty
.inner
)))
585 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
589 # Build a new `MProperty` from a `node`.
591 # REQUIRE `node.labels.has("MProperty")`
592 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
593 var m
= mentities
.get_or_null
(node
.id
.as(Int))
594 if m
isa MProperty then return m
596 assert node
.labels
.has
("MProperty")
597 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
598 var name
= node
["name"].to_s
599 var location
= to_location
(node
["location"].to_s
)
600 var visibility
= to_visibility
(node
["visibility"].to_s
)
601 var mprop
: nullable MProperty = null
602 if node
.labels
.has
("MMethod") then
603 mprop
= new MMethod(intro_mclassdef
, name
, location
, visibility
)
604 mprop
.is_init
= node
["is_init"].as(Bool)
605 else if node
.labels
.has
("MAttribute") then
606 mprop
= new MAttribute(intro_mclassdef
, name
, location
, visibility
)
607 else if node
.labels
.has
("MVirtualTypeProp") then
608 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, location
, visibility
)
609 else if node
.labels
.has
("MInnerClass") then
610 var inner
= to_mclass
(model
, node
.out_nodes
("NESTS").first
)
611 mprop
= new MInnerClass(intro_mclassdef
, name
, location
, visibility
, inner
)
613 if mprop
== null then
614 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
617 mentities
[node
.id
.as(Int)] = mprop
622 # Build a `NeoNode` representing `mpropdef`.
623 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
624 var node
= make_node
(mpropdef
)
625 node
.labels
.add
"MPropDef"
626 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
627 if mpropdef
isa MMethodDef then
628 node
.labels
.add
"MMethodDef"
629 node
["is_abstract"] = mpropdef
.is_abstract
630 node
["is_intern"] = mpropdef
.is_intern
631 node
["is_extern"] = mpropdef
.is_extern
632 var msignature
= mpropdef
.msignature
633 if msignature
!= null then
634 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
636 else if mpropdef
isa MAttributeDef then
637 node
.labels
.add
"MAttributeDef"
638 var static_mtype
= mpropdef
.static_mtype
639 if static_mtype
!= null then
640 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(static_mtype
)))
642 else if mpropdef
isa MVirtualTypeDef then
643 node
.labels
.add
"MVirtualTypeDef"
644 var bound
= mpropdef
.bound
645 if bound
!= null then
646 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
648 else if mpropdef
isa MInnerClassDef then
649 node
.labels
.add
"MInnerClassDef"
650 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mpropdef
.inner
)))
655 # Build a new `MPropDef` from a `node`.
657 # REQUIRE `node.labels.has("MPropDef")`
658 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
659 var m
= mentities
.get_or_null
(node
.id
.as(Int))
660 if m
isa MPropDef then return m
662 assert node
.labels
.has
("MPropDef")
663 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
664 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
665 var location
= to_location
(node
["location"].to_s
)
666 var mpropdef
: nullable MPropDef = null
667 if node
.labels
.has
("MMethodDef") then
668 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
669 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
670 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
671 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
672 mentities
[node
.id
.as(Int)] = mpropdef
673 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
674 else if node
.labels
.has
("MAttributeDef") then
675 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
676 mentities
[node
.id
.as(Int)] = mpropdef
677 var static_mtype
= node
.out_nodes
("TYPE")
678 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
679 else if node
.labels
.has
("MVirtualTypeDef") then
680 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
681 mentities
[node
.id
.as(Int)] = mpropdef
682 var bound
= node
.out_nodes
("BOUND")
683 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
684 else if node
.labels
.has
("MInnerClassDef") then
685 var inner
= to_mclassdef
(model
, node
.out_nodes
("NESTS").first
)
686 mpropdef
= new MInnerClassDef(mclassdef
,
687 mproperty
.as(MInnerClass), location
, inner
)
688 mentities
[node
.id
.as(Int)] = mpropdef
690 if mpropdef
== null then
691 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
694 set_doc
(node
, mpropdef
)
698 # Build a `NeoNode` representing `mtype`.
699 private fun mtype_node
(mtype
: MType): NeoNode do
700 var node
= make_node
(mtype
)
701 node
.labels
.add
"MType"
702 if mtype
isa MClassType then
703 node
.labels
.add
"MClassType"
704 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
705 for arg
in mtype
.arguments
do
706 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
708 if mtype
isa MGenericType then
709 node
.labels
.add
"MGenericType"
711 else if mtype
isa MVirtualType then
712 node
.labels
.add
"MVirtualType"
713 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
714 else if mtype
isa MParameterType then
715 node
.labels
.add
"MParameterType"
716 node
["rank"] = mtype
.rank
717 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
718 else if mtype
isa MNullableType then
719 node
.labels
.add
"MNullableType"
720 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
721 else if mtype
isa MSignature then
722 node
.labels
.add
"MSignature"
723 var names
= new JsonArray
725 for mparameter
in mtype
.mparameters
do
726 names
.add mparameter
.name
727 var pnode
= mparameter_node
(mparameter
)
729 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
732 if not names
.is_empty
then node
["parameter_names"] = names
733 var return_mtype
= mtype
.return_mtype
734 if return_mtype
!= null then
735 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
737 else if mtype
isa MRawType then
738 node
.labels
.add
"MRawType"
739 var text
= new JsonArray
741 for part
in mtype
.parts
do
743 if part
.target
!= null then
744 var pnode
= mtypepart_node
(part
)
746 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
750 if not text
.is_empty
then node
["text"] = text
755 # Build a `NeoNode` representing `mtypepart`.
756 private fun mtypepart_node
(mtypepart
: MTypePart): NeoNode do
757 var node
= make_node
(mtypepart
)
758 node
.labels
.add
"MTypePart"
759 if mtypepart
.target
!= null then
760 var target_node
= to_node
(mtypepart
.target
.as(not null))
761 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
766 # Build a new `MType` from a `node`.
768 # REQUIRE `node.labels.has("MType")`
769 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
770 var m
= mentities
.get_or_null
(node
.id
.as(Int))
771 if m
isa MType then return m
773 assert node
.labels
.has
("MType")
774 if node
.labels
.has
("MClassType") then
775 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
776 var args
= new Array[MType]
777 for narg
in node
.out_nodes
("ARGUMENT") do
778 args
.add to_mtype
(model
, narg
)
780 var mtype
= mclass
.get_mtype
(args
)
781 mentities
[node
.id
.as(Int)] = mtype
783 else if node
.labels
.has
("MParameterType") then
784 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
785 var rank
= node
["rank"].to_s
.to_i
786 var mtype
= mclass
.mparameters
[rank
]
787 mentities
[node
.id
.as(Int)] = mtype
789 else if node
.labels
.has
("MNullableType") then
790 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
791 var mtype
= intype
.as_nullable
792 mentities
[node
.id
.as(Int)] = mtype
794 else if node
.labels
.has
("MVirtualType") then
795 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
796 assert mproperty
isa MVirtualTypeProp
797 var mtype
= mproperty
.mvirtualtype
798 mentities
[node
.id
.as(Int)] = mtype
800 else if node
.labels
.has
("MSignature") then
801 # Get all param nodes
802 var mparam_nodes
= new HashMap[String, MParameter]
803 for pnode
in node
.out_nodes
("PARAMETER") do
804 var mparam
= to_mparameter
(model
, pnode
)
805 mparam_nodes
[mparam
.name
] = mparam
807 # Load params in the good order
808 var mparam_names
= node
["parameter_names"]
809 var mparameters
= new Array[MParameter]
810 if mparam_names
isa JsonArray then
811 for mparam_name
in mparam_names
do
812 var mparam
= mparam_nodes
[mparam_name
.to_s
]
813 mparameters
.add mparam
816 var return_mtype
: nullable MType = null
817 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
818 if not ret_nodes
.is_empty
then
819 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
821 var mtype
= new MSignature(mparameters
, return_mtype
)
822 mentities
[node
.id
.as(Int)] = mtype
824 else if node
.labels
.has
("MRawType") then
825 var mtype
= new MRawType(model
)
826 var parts
= node
["text"]
827 if parts
isa JsonArray then
829 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
831 for pnode
in node
.out_nodes
("LINK") do
832 assert pnode
.labels
.has
("MTypePart")
833 if not pnode
.out_nodes
("TARGET").is_empty
then
834 var rank
= pnode
["rank"]
835 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
837 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
841 mentities
[node
.id
.as(Int)] = mtype
844 print
"not yet implemented to_mtype for {node.labels.join(",")}"
848 # Build a `NeoNode` representing `mparameter`.
849 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
850 var node
= make_node
(mparameter
)
851 node
.labels
.add
"MParameter"
852 node
["name"] = mparameter
.name
853 node
["is_vararg"] = mparameter
.is_vararg
854 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
858 # Build a new `MParameter` from `node`.
860 # REQUIRE `node.labels.has("MParameter")`
861 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
862 var m
= mentities
.get_or_null
(node
.id
.as(Int))
863 if m
isa MParameter then return m
865 assert node
.labels
.has
("MParameter")
866 var name
= node
["name"].to_s
867 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
868 var is_vararg
= node
["is_vararg"].as(Bool)
869 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
870 mentities
[node
.id
.as(Int)] = mparameter
874 # Get a `Location` from its string representation.
875 private fun to_location
(loc
: String): nitc
::Location do
876 return new nitc
::Location.from_string
(loc
)
879 # Get a `MVisibility` from its string representation.
880 private fun to_visibility
(vis
: String): MVisibility do
881 if vis
== intrude_visibility
.to_s
then
882 return intrude_visibility
883 else if vis
== public_visibility
.to_s
then
884 return public_visibility
885 else if vis
== protected_visibility
.to_s
then
886 return protected_visibility
887 else if vis
== private_visibility
.to_s
then
888 return private_visibility
889 else if vis
== package_visibility
.to_s
then
890 return package_visibility
892 return none_visibility
896 # Get a `MKind` from its string representation.
897 private fun to_kind
(kind
: String): MClassKind do
898 if kind
== abstract_kind
.to_s
then
900 else if kind
== concrete_kind
.to_s
then
902 else if kind
== interface_kind
.to_s
then
903 return interface_kind
904 else if kind
== enum_kind
.to_s
then
906 else if kind
== extern_kind
.to_s
then
909 return raw_kind
(kind
)
913 # Extract the `MDoc` from `node` and link it to `mentity`.
914 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
915 if node
.has_key
("mdoc") then
916 var lines
= new Array[String]
917 for e
in node
["mdoc"].as(JsonArray) do
918 lines
.add e
.to_s
#.replace("\n", "\\n")
920 var location
= to_location
(node
["mdoc_location"].to_s
)
921 var mdoc
= new MDoc(location
)
922 mdoc
.content
.add_all
(lines
)
923 mdoc
.original_mentity
= mentity