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 mentity
.mdoc
!= null then
369 node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
370 node
["mdoc_location"] = mentity
.mdoc
.location
.to_s
375 # Build a `NeoNode` representing `mpackage`.
376 private fun mpackage_node
(mpackage
: MPackage): NeoNode do
377 var node
= make_node
(mpackage
)
378 node
.labels
.add
"MPackage"
379 var root
= mpackage
.root
381 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
386 # Build a new `MPackage` from a `node`.
388 # REQUIRE `node.labels.has("MPackage")`
389 private fun to_mpackage
(model
: Model, node
: NeoNode): MPackage do
390 var m
= mentities
.get_or_null
(node
.id
.as(Int))
391 if m
isa MPackage then return m
393 assert node
.labels
.has
("MPackage")
394 var mpackage
= new MPackage(node
["name"].to_s
, model
)
395 mentities
[node
.id
.as(Int)] = mpackage
396 set_doc
(node
, mpackage
)
397 mpackage
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
401 # Build a `NeoNode` representing `mgroup`.
402 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
403 var node
= make_node
(mgroup
)
404 node
.labels
.add
"MGroup"
405 var parent
= mgroup
.parent
406 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mpackage
)))
407 if parent
!= null then
408 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
410 for mmodule
in mgroup
.mmodules
do
411 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
413 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
414 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
419 # Build a new `MGroup` from a `node`.
421 # REQUIRE `node.labels.has("MGroup")`
422 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
423 var m
= mentities
.get_or_null
(node
.id
.as(Int))
424 if m
isa MGroup then return m
426 assert node
.labels
.has
("MGroup")
427 var mpackage
= to_mpackage
(model
, node
.out_nodes
("PROJECT").first
)
428 var parent
: nullable MGroup = null
429 var out
= node
.out_nodes
("PARENT")
430 if not out
.is_empty
then
431 parent
= to_mgroup
(model
, out
.first
)
433 var mgroup
= new MGroup(node
["name"].to_s
, mpackage
, parent
)
434 mentities
[node
.id
.as(Int)] = mgroup
435 set_doc
(node
, mgroup
)
439 # Build a `NeoNode` representing `mmodule`.
440 private fun mmodule_node
(mmodule
: MModule): NeoNode do
441 var node
= make_node
(mmodule
)
442 node
.labels
.add
"MModule"
443 node
["location"] = mmodule
.location
.to_s
444 for parent
in mmodule
.in_importation
.direct_greaters
do
445 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
447 for mclass
in mmodule
.intro_mclasses
do
448 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
450 for mclassdef
in mmodule
.mclassdefs
do
451 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
456 # Build a new `MModule` from a `node`.
458 # REQUIRE `node.labels.has("MModule")`
459 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
460 var m
= mentities
.get_or_null
(node
.id
.as(Int))
461 if m
isa MModule then return m
463 assert node
.labels
.has
("MModule")
464 var ins
= node
.in_nodes
("DECLARES")
465 var mgroup
: nullable MGroup = null
466 if not ins
.is_empty
then
467 mgroup
= to_mgroup
(model
, ins
.first
)
469 var name
= node
["name"].to_s
470 var location
= to_location
(node
["location"].to_s
)
471 var mmodule
= new MModule(model
, mgroup
, name
, location
)
472 mentities
[node
.id
.as(Int)] = mmodule
473 set_doc
(node
, mmodule
)
474 var imported_mmodules
= new Array[MModule]
475 for smod
in node
.out_nodes
("IMPORTS") do
476 imported_mmodules
.add to_mmodule
(model
, smod
)
478 mmodule
.set_imported_mmodules
(imported_mmodules
)
482 # Build a `NeoNode` representing `mclass`.
483 private fun mclass_node
(mclass
: MClass): NeoNode do
484 var node
= make_node
(mclass
)
485 node
.labels
.add
"MClass"
486 node
["kind"] = mclass
.kind
.to_s
487 node
["visibility"] = mclass
.visibility
.to_s
488 if not mclass
.mparameters
.is_empty
then
489 var parameter_names
= new Array[String]
490 for p
in mclass
.mparameters
do parameter_names
.add
(p
.name
)
491 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
493 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
497 # Build a new `MClass` from a `node`.
499 # REQUIRE `node.labels.has("MClass")`
500 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
501 var m
= mentities
.get_or_null
(node
.id
.as(Int))
502 if m
isa MClass then return m
504 assert node
.labels
.has
("MClass")
505 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
506 var name
= node
["name"].to_s
507 var kind
= to_kind
(node
["kind"].to_s
)
508 var visibility
= to_visibility
(node
["visibility"].to_s
)
509 var parameter_names
= new Array[String]
510 if node
.has_key
("parameter_names") then
511 for e
in node
["parameter_names"].as(JsonArray) do
512 parameter_names
.add e
.to_s
515 var mclass
= new MClass(mmodule
, name
, parameter_names
, kind
, visibility
)
516 mentities
[node
.id
.as(Int)] = mclass
517 set_doc
(node
, mclass
)
521 # Build a `NeoNode` representing `mclassdef`.
522 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
523 var node
= make_node
(mclassdef
)
524 node
.labels
.add
"MClassDef"
525 node
["location"] = mclassdef
.location
.to_s
526 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
527 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
528 for mproperty
in mclassdef
.intro_mproperties
do
529 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
531 for mpropdef
in mclassdef
.mpropdefs
do
532 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
534 for sup
in mclassdef
.supertypes
do
535 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
540 # Build a new `MClassDef` from a `node`.
542 # REQUIRE `node.labels.has("MClassDef")`
543 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
544 var m
= mentities
.get_or_null
(node
.id
.as(Int))
545 if m
isa MClassDef then return m
547 assert node
.labels
.has
("MClassDef")
548 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
549 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
550 var location
= to_location
(node
["location"].to_s
)
551 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
552 mentities
[node
.id
.as(Int)] = mclassdef
553 set_doc
(node
, mclassdef
)
554 var supertypes
= new Array[MClassType]
555 for sup
in node
.out_nodes
("INHERITS") do
556 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
558 mclassdef
.set_supertypes
(supertypes
)
559 mclassdef
.add_in_hierarchy
563 # Build a `NeoNode` representing `mproperty`.
564 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
565 var node
= make_node
(mproperty
)
566 node
.labels
.add
"MProperty"
567 node
["visibility"] = mproperty
.visibility
.to_s
568 if mproperty
isa MMethod then
569 node
.labels
.add
"MMethod"
570 node
["is_init"] = mproperty
.is_init
571 else if mproperty
isa MAttribute then
572 node
.labels
.add
"MAttribute"
573 else if mproperty
isa MVirtualTypeProp then
574 node
.labels
.add
"MVirtualTypeProp"
575 else if mproperty
isa MInnerClass then
576 node
.labels
.add
"MInnerClass"
577 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mproperty
.inner
)))
579 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
583 # Build a new `MProperty` from a `node`.
585 # REQUIRE `node.labels.has("MProperty")`
586 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
587 var m
= mentities
.get_or_null
(node
.id
.as(Int))
588 if m
isa MProperty then return m
590 assert node
.labels
.has
("MProperty")
591 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
592 var name
= node
["name"].to_s
593 var visibility
= to_visibility
(node
["visibility"].to_s
)
594 var mprop
: nullable MProperty = null
595 if node
.labels
.has
("MMethod") then
596 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
597 mprop
.is_init
= node
["is_init"].as(Bool)
598 else if node
.labels
.has
("MAttribute") then
599 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
600 else if node
.labels
.has
("MVirtualTypeProp") then
601 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
602 else if node
.labels
.has
("MInnerClass") then
603 var inner
= to_mclass
(model
, node
.out_nodes
("NESTS").first
)
604 mprop
= new MInnerClass(intro_mclassdef
, name
, visibility
, inner
)
606 if mprop
== null then
607 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
610 mentities
[node
.id
.as(Int)] = mprop
615 # Build a `NeoNode` representing `mpropdef`.
616 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
617 var node
= make_node
(mpropdef
)
618 node
.labels
.add
"MPropDef"
619 node
["location"] = mpropdef
.location
.to_s
620 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
621 if mpropdef
isa MMethodDef then
622 node
.labels
.add
"MMethodDef"
623 node
["is_abstract"] = mpropdef
.is_abstract
624 node
["is_intern"] = mpropdef
.is_intern
625 node
["is_extern"] = mpropdef
.is_extern
626 var msignature
= mpropdef
.msignature
627 if msignature
!= null then
628 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
630 else if mpropdef
isa MAttributeDef then
631 node
.labels
.add
"MAttributeDef"
632 var static_mtype
= mpropdef
.static_mtype
633 if static_mtype
!= null then
634 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(static_mtype
)))
636 else if mpropdef
isa MVirtualTypeDef then
637 node
.labels
.add
"MVirtualTypeDef"
638 var bound
= mpropdef
.bound
639 if bound
!= null then
640 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
642 else if mpropdef
isa MInnerClassDef then
643 node
.labels
.add
"MInnerClassDef"
644 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mpropdef
.inner
)))
649 # Build a new `MPropDef` from a `node`.
651 # REQUIRE `node.labels.has("MPropDef")`
652 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
653 var m
= mentities
.get_or_null
(node
.id
.as(Int))
654 if m
isa MPropDef then return m
656 assert node
.labels
.has
("MPropDef")
657 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
658 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
659 var location
= to_location
(node
["location"].to_s
)
660 var mpropdef
: nullable MPropDef = null
661 if node
.labels
.has
("MMethodDef") then
662 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
663 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
664 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
665 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
666 mentities
[node
.id
.as(Int)] = mpropdef
667 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
668 else if node
.labels
.has
("MAttributeDef") then
669 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
670 mentities
[node
.id
.as(Int)] = mpropdef
671 var static_mtype
= node
.out_nodes
("TYPE")
672 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
673 else if node
.labels
.has
("MVirtualTypeDef") then
674 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
675 mentities
[node
.id
.as(Int)] = mpropdef
676 var bound
= node
.out_nodes
("BOUND")
677 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
678 else if node
.labels
.has
("MInnerClassDef") then
679 var inner
= to_mclassdef
(model
, node
.out_nodes
("NESTS").first
)
680 mpropdef
= new MInnerClassDef(mclassdef
,
681 mproperty
.as(MInnerClass), location
, inner
)
682 mentities
[node
.id
.as(Int)] = mpropdef
684 if mpropdef
== null then
685 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
688 set_doc
(node
, mpropdef
)
692 # Build a `NeoNode` representing `mtype`.
693 private fun mtype_node
(mtype
: MType): NeoNode do
694 var node
= make_node
(mtype
)
695 node
.labels
.add
"MType"
696 if mtype
isa MClassType then
697 node
.labels
.add
"MClassType"
698 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
699 for arg
in mtype
.arguments
do
700 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
702 if mtype
isa MGenericType then
703 node
.labels
.add
"MGenericType"
705 else if mtype
isa MVirtualType then
706 node
.labels
.add
"MVirtualType"
707 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
708 else if mtype
isa MParameterType then
709 node
.labels
.add
"MParameterType"
710 node
["rank"] = mtype
.rank
711 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
712 else if mtype
isa MNullableType then
713 node
.labels
.add
"MNullableType"
714 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
715 else if mtype
isa MSignature then
716 node
.labels
.add
"MSignature"
717 var names
= new JsonArray
719 for mparameter
in mtype
.mparameters
do
720 names
.add mparameter
.name
721 var pnode
= mparameter_node
(mparameter
)
723 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
726 if not names
.is_empty
then node
["parameter_names"] = names
727 var return_mtype
= mtype
.return_mtype
728 if return_mtype
!= null then
729 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
731 else if mtype
isa MRawType then
732 node
.labels
.add
"MRawType"
733 var text
= new JsonArray
735 for part
in mtype
.parts
do
737 if part
.target
!= null then
738 var pnode
= mtypepart_node
(part
)
740 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
744 if not text
.is_empty
then node
["text"] = text
749 # Build a `NeoNode` representing `mtypepart`.
750 private fun mtypepart_node
(mtypepart
: MTypePart): NeoNode do
751 var node
= make_node
(mtypepart
)
752 node
.labels
.add
"MTypePart"
753 if mtypepart
.target
!= null then
754 var target_node
= to_node
(mtypepart
.target
.as(not null))
755 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
760 # Build a new `MType` from a `node`.
762 # REQUIRE `node.labels.has("MType")`
763 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
764 var m
= mentities
.get_or_null
(node
.id
.as(Int))
765 if m
isa MType then return m
767 assert node
.labels
.has
("MType")
768 if node
.labels
.has
("MClassType") then
769 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
770 var args
= new Array[MType]
771 for narg
in node
.out_nodes
("ARGUMENT") do
772 args
.add to_mtype
(model
, narg
)
774 var mtype
= mclass
.get_mtype
(args
)
775 mentities
[node
.id
.as(Int)] = mtype
777 else if node
.labels
.has
("MParameterType") then
778 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
779 var rank
= node
["rank"].to_s
.to_i
780 var mtype
= mclass
.mparameters
[rank
]
781 mentities
[node
.id
.as(Int)] = mtype
783 else if node
.labels
.has
("MNullableType") then
784 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
785 var mtype
= intype
.as_nullable
786 mentities
[node
.id
.as(Int)] = mtype
788 else if node
.labels
.has
("MVirtualType") then
789 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
790 assert mproperty
isa MVirtualTypeProp
791 var mtype
= mproperty
.mvirtualtype
792 mentities
[node
.id
.as(Int)] = mtype
794 else if node
.labels
.has
("MSignature") then
795 # Get all param nodes
796 var mparam_nodes
= new HashMap[String, MParameter]
797 for pnode
in node
.out_nodes
("PARAMETER") do
798 var mparam
= to_mparameter
(model
, pnode
)
799 mparam_nodes
[mparam
.name
] = mparam
801 # Load params in the good order
802 var mparam_names
= node
["parameter_names"]
803 var mparameters
= new Array[MParameter]
804 if mparam_names
isa JsonArray then
805 for mparam_name
in mparam_names
do
806 var mparam
= mparam_nodes
[mparam_name
.to_s
]
807 mparameters
.add mparam
810 var return_mtype
: nullable MType = null
811 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
812 if not ret_nodes
.is_empty
then
813 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
815 var mtype
= new MSignature(mparameters
, return_mtype
)
816 mentities
[node
.id
.as(Int)] = mtype
818 else if node
.labels
.has
("MRawType") then
819 var mtype
= new MRawType(model
)
820 var parts
= node
["text"]
821 if parts
isa JsonArray then
823 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
825 for pnode
in node
.out_nodes
("LINK") do
826 assert pnode
.labels
.has
("MTypePart")
827 if not pnode
.out_nodes
("TARGET").is_empty
then
828 var rank
= pnode
["rank"]
829 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
831 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
835 mentities
[node
.id
.as(Int)] = mtype
838 print
"not yet implemented to_mtype for {node.labels.join(",")}"
842 # Build a `NeoNode` representing `mparameter`.
843 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
844 var node
= make_node
(mparameter
)
845 node
.labels
.add
"MParameter"
846 node
["name"] = mparameter
.name
847 node
["is_vararg"] = mparameter
.is_vararg
848 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
852 # Build a new `MParameter` from `node`.
854 # REQUIRE `node.labels.has("MParameter")`
855 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
856 var m
= mentities
.get_or_null
(node
.id
.as(Int))
857 if m
isa MParameter then return m
859 assert node
.labels
.has
("MParameter")
860 var name
= node
["name"].to_s
861 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
862 var is_vararg
= node
["is_vararg"].as(Bool)
863 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
864 mentities
[node
.id
.as(Int)] = mparameter
868 # Get a `Location` from its string representation.
869 private fun to_location
(loc
: String): Location do
870 return new Location.from_string
(loc
)
873 # Get a `MVisibility` from its string representation.
874 private fun to_visibility
(vis
: String): MVisibility do
875 if vis
== intrude_visibility
.to_s
then
876 return intrude_visibility
877 else if vis
== public_visibility
.to_s
then
878 return public_visibility
879 else if vis
== protected_visibility
.to_s
then
880 return protected_visibility
881 else if vis
== private_visibility
.to_s
then
882 return private_visibility
883 else if vis
== package_visibility
.to_s
then
884 return package_visibility
886 return none_visibility
890 # Get a `MKind` from its string representation.
891 private fun to_kind
(kind
: String): MClassKind do
892 if kind
== abstract_kind
.to_s
then
894 else if kind
== concrete_kind
.to_s
then
896 else if kind
== interface_kind
.to_s
then
897 return interface_kind
898 else if kind
== enum_kind
.to_s
then
900 else if kind
== extern_kind
.to_s
then
903 return raw_kind
(kind
)
907 # Extract the `MDoc` from `node` and link it to `mentity`.
908 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
909 if node
.has_key
("mdoc") then
910 var lines
= new Array[String]
911 for e
in node
["mdoc"].as(JsonArray) do
912 lines
.add e
.to_s
#.replace("\n", "\\n")
914 var location
= to_location
(node
["mdoc_location"].to_s
)
915 var mdoc
= new MDoc(location
)
916 mdoc
.content
.add_all
(lines
)
917 mdoc
.original_mentity
= mentity