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: `MProject`, `model_name` and `MEntity`.
41 # * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree.
45 # * labels: `MGroup`, `model_name` and `MEntity`.
46 # * `full_name`: fully qualified name.
47 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
48 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
50 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
52 # * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
57 # * labels: `MModule`, `model_name` and `MEntity`.
58 # * `full_name`: fully qualified name.
59 # * `location`: origin of the definition. SEE: `Location.to_s`
60 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
61 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
63 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
68 # * labels: `MClass`, `model_name` and `MEntity`.
69 # * `full_name`: fully qualified name.
70 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
71 # * `visibility`: visibility of the class.
72 # * `parameter_names`: JSON array listing the name of each formal generic
73 # parameter (in order of declaration).
74 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
76 # Arguments in the `CLASSTYPE` are named following the `parameter_names`
77 # attribute of the `MClassDef` that introduces the class. A class definition
78 # introduces a class if and only if it has this class as `MCLASS` and
79 # has `is_intro` set to `true`.
83 # * labels: `MClassDef`, `model_name` and `MEntity`.
84 # * `location`: origin of the definition. SEE: `Location.to_s`
85 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
87 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
88 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
90 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
91 # classdef (introductions and redefinitions).
92 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
96 # * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
97 # `MAttribute` `MVirtualTypeProp` or `MInnerClass`, depending on the class of
98 # the represented entity.
99 # * `full_name`: fully qualified name.
100 # * `visibility`: visibility of the property.
101 # * `is_init`: Indicates if the property is a constructor. Exists only if the
102 # node is a `MMethod`.
103 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
106 # Additional relationship for `MInnerClass`:
108 # * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
112 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
113 # `MAttributeDef`, `MVirtualTypeDef` or `MInnerClassDef`, depending on the
114 # class of the represented entity.
115 # * `location`: origin of the definition. SEE: `Location.to_s`.
116 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
118 # Additional attributes and relationship for `MMethodDef`:
120 # * `is_abstract`: Is the method definition abstract?
121 # * `is_intern`: Is the method definition intern?
122 # * `is_extern`: Is the method definition extern?
123 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
124 # property definition.
126 # Additional relationship for `MAttributeDef`:
128 # * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
131 # Additional relationship for `MVirtualTypeDef`:
133 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
134 # is bound in this definition. Exists only if this definition bound the virtual
135 # type to an effective type.
137 # Additional relationship for `MInnerClassDef`:
139 # * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
144 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
145 # `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
146 # class of the represented entity.
148 # Additional label and relationships for `MClassType`:
150 # * If it is a `MGenericType`, also has the `MGenericType` label.
151 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
152 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
154 # Arguments are named following the `parameter_names` attribute of the
155 # `MClass` referred by `CLASS`.
157 # Additional relationship for `MVirtualType`:
159 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
160 # determines the type (usually a `MVirtualTypeProp`).
162 # Additional attribute and relationship for `MParameterType`:
164 # * `rank`: position of the parameter (0 for the first parameter).
165 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
168 # Additional relationship for `MNullableType`:
170 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
172 # Additional attribute and relationship for `MRawType`:
174 # * `text`: JSON array of the parts’ text.
175 # * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
177 # Additional attribute and relationships for `MSignature`:
179 # * `parameter_names`: JSON array representing the list of the parameter names.
180 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
181 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
184 # In order to maintain the correct parameters order, each `MSignature` node
185 # contains an array of parameter names corresponding to the parameter order in
188 # For example, if the source code contains:
191 # fun foo(a: A, b: B, c: C)
194 # The `MSignature` node will contain a property
195 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
196 # with the parameters in the correct order.
200 # * labels: `MParameter`, `model_name` and `MEntity`.
201 # * `is_vararg`: Is the parameter a vararg?
202 # * `rank`: position of the parameter (0 for the first parameter).
203 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
205 # MParameters are also ranked by their position in the corresponding signature.
206 # Rank 0 for the first parameter, 1 for the next one and etc.
210 # * labels: `MTypePart`, `model_name` and `MEntity`.
211 # * `rank`: position in the `MRawType`.
212 # * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
215 import doc
::model_ext
219 # Helper class that can save and load a `Model` into a Neo4j database.
224 # Because we use only one Neo4j instance to store all the models,
225 # we need to mark their appartenance to a particular model and avoid loading all models.
227 # The name is used as a Neo label on each created nodes and used to load nodes from base.
228 var model_name
: String
230 # The toolcontext used to init the `NeoModel` tool.
231 var toolcontext
: ToolContext
233 # The Neo4j `client` used to communicate with the Neo4j instance.
234 var client
: Neo4jClient
236 # Fill `model` using base pointed by `client`.
237 fun load
(model
: Model): Model do
238 var nodes
: Array[NeoNode]
240 toolcontext
.info
("Loading project node...", 1)
241 nodes
= client
.nodes_with_labels
([model_name
, "MProject"])
242 for node
in nodes
do to_mproject
(model
, node
)
243 toolcontext
.info
("Loading groups...", 1)
244 nodes
= client
.nodes_with_labels
([model_name
, "MGroup"])
245 for node
in nodes
do to_mgroup
(model
, node
)
246 toolcontext
.info
("Loading modules...", 1)
247 nodes
= client
.nodes_with_labels
([model_name
, "MModule"])
248 for node
in nodes
do to_mmodule
(model
, node
)
249 toolcontext
.info
("Loading classes...", 1)
250 nodes
= client
.nodes_with_labels
([model_name
, "MClass"])
251 for node
in nodes
do to_mclass
(model
, node
)
252 toolcontext
.info
("Loading class definitions...", 1)
253 nodes
= client
.nodes_with_labels
([model_name
, "MClassDef"])
254 for node
in nodes
do to_mclassdef
(model
, node
)
255 toolcontext
.info
("Loading properties...", 1)
256 nodes
= client
.nodes_with_labels
([model_name
, "MProperty"])
257 for node
in nodes
do to_mproperty
(model
, node
)
258 toolcontext
.info
("Loading property definitions...", 1)
259 nodes
= client
.nodes_with_labels
([model_name
, "MPropDef"])
260 for node
in nodes
do to_mpropdef
(model
, node
)
264 # Save `model` in the base pointed by `client`.
265 fun save
(model
: Model) do
266 var nodes
= collect_model_nodes
(model
)
267 toolcontext
.info
("Save {nodes.length} nodes...", 1)
269 var edges
= collect_model_edges
(model
)
270 toolcontext
.info
("Save {edges.length} edges...", 1)
274 # Save `neo_entities` in base using batch mode.
275 private fun push_all
(neo_entities
: Collection[NeoEntity]) do
276 var batch
= new NeoBatch(client
)
277 var len
= neo_entities
.length
280 for nentity
in neo_entities
do
281 batch
.save_entity
(nentity
)
282 if i
== batch_max_size
then
284 sum
+= batch_max_size
285 toolcontext
.info
(" {sum * 100 / len}% done", 1)
286 batch
= new NeoBatch(client
)
295 # How many operation can be executed in one batch?
296 private var batch_max_size
= 1000
298 # Execute `batch` and check for errors.
300 # Abort if `batch.execute` returns errors.
301 private fun do_batch
(batch
: NeoBatch) do
302 var errors
= batch
.execute
303 if not errors
.is_empty
then
309 # Collect all nodes from the current `model`.
310 private fun collect_model_nodes
(model
: Model): Collection[NeoNode] do
311 for mproject
in model
.mprojects
do
313 for mgroup
in mproject
.mgroups
do to_node
(mgroup
)
318 # Collect all edges from the current `model`.
320 # Actually collect all out_edges from all nodes.
321 private fun collect_model_edges
(model
: Model): Collection[NeoEdge] do
322 var edges
= new HashSet[NeoEdge]
323 for node
in nodes
.values
do edges
.add_all
(node
.out_edges
)
327 # Mentities associated to nodes.
329 # The key is the node’s id.
330 private var mentities
= new HashMap[Int, MEntity]
332 # Nodes associated with MEntities.
333 private var nodes
= new HashMap[MEntity, NeoNode]
335 # Get the `NeoNode` associated with `mentity`.
336 # `mentities` are stored locally to avoid duplication.
337 fun to_node
(mentity
: MEntity): NeoNode do
338 if nodes
.has_key
(mentity
) then return nodes
[mentity
]
339 if mentity
isa MProject then return mproject_node
(mentity
)
340 if mentity
isa MGroup then return mgroup_node
(mentity
)
341 if mentity
isa MModule then return mmodule_node
(mentity
)
342 if mentity
isa MClass then return mclass_node
(mentity
)
343 if mentity
isa MClassDef then return mclassdef_node
(mentity
)
344 if mentity
isa MProperty then return mproperty_node
(mentity
)
345 if mentity
isa MPropDef then return mpropdef_node
(mentity
)
346 if mentity
isa MType then return mtype_node
(mentity
)
347 if mentity
isa MParameter then return mparameter_node
(mentity
)
351 # Get the `MEntity` associated with `node`.
352 fun to_mentity
(model
: Model, node
: NeoNode): MEntity do
353 if node
.labels
.has
("MProject") then return to_mproject
(model
, node
)
354 if node
.labels
.has
("MGroup") then return to_mgroup
(model
, node
)
355 if node
.labels
.has
("MModule") then return to_mmodule
(model
, node
)
356 if node
.labels
.has
("MClass") then return to_mclass
(model
, node
)
357 if node
.labels
.has
("MClassDef") then return to_mclassdef
(model
, node
)
358 if node
.labels
.has
("MProperty") then return to_mproperty
(model
, node
)
359 if node
.labels
.has
("MPropDef") then return to_mpropdef
(model
, node
)
360 if node
.labels
.has
("MType") then return to_mtype
(model
, node
)
361 if node
.labels
.has
("MParameter") then return to_mparameter
(model
, node
)
365 # Make a new `NeoNode` based on `mentity`.
366 private fun make_node
(mentity
: MEntity): NeoNode do
367 var node
= new NeoNode
368 nodes
[mentity
] = node
369 node
.labels
.add
"MEntity"
370 node
.labels
.add model_name
371 node
["name"] = mentity
.name
372 if mentity
.mdoc
!= null then node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
376 # Build a `NeoNode` representing `mproject`.
377 private fun mproject_node
(mproject
: MProject): NeoNode do
378 var node
= make_node
(mproject
)
379 node
.labels
.add
"MProject"
380 var root
= mproject
.root
382 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
387 # Build a new `MProject` from a `node`.
389 # REQUIRE `node.labels.has("MProject")`
390 private fun to_mproject
(model
: Model, node
: NeoNode): MProject do
391 var m
= mentities
.get_or_null
(node
.id
.as(Int))
392 if m
isa MProject then return m
394 assert node
.labels
.has
("MProject")
395 var mproject
= new MProject(node
["name"].to_s
, model
)
396 mentities
[node
.id
.as(Int)] = mproject
397 set_doc
(node
, mproject
)
398 mproject
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
402 # Build a `NeoNode` representing `mgroup`.
403 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
404 var node
= make_node
(mgroup
)
405 node
.labels
.add
"MGroup"
406 node
["full_name"] = mgroup
.full_name
407 var parent
= mgroup
.parent
408 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mproject
)))
409 if parent
!= null then
410 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
412 for mmodule
in mgroup
.mmodules
do
413 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
415 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
416 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
421 # Build a new `MGroup` from a `node`.
423 # REQUIRE `node.labels.has("MGroup")`
424 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
425 var m
= mentities
.get_or_null
(node
.id
.as(Int))
426 if m
isa MGroup then return m
428 assert node
.labels
.has
("MGroup")
429 var mproject
= to_mproject
(model
, node
.out_nodes
("PROJECT").first
)
430 var parent
: nullable MGroup = null
431 var out
= node
.out_nodes
("PARENT")
432 if not out
.is_empty
then
433 parent
= to_mgroup
(model
, out
.first
)
435 var mgroup
= new MGroup(node
["name"].to_s
, mproject
, parent
)
436 mentities
[node
.id
.as(Int)] = mgroup
437 set_doc
(node
, mgroup
)
441 # Build a `NeoNode` representing `mmodule`.
442 private fun mmodule_node
(mmodule
: MModule): NeoNode do
443 var node
= make_node
(mmodule
)
444 node
.labels
.add
"MModule"
445 node
["full_name"] = mmodule
.full_name
446 node
["location"] = mmodule
.location
.to_s
447 for parent
in mmodule
.in_importation
.direct_greaters
do
448 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
450 for mclass
in mmodule
.intro_mclasses
do
451 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
453 for mclassdef
in mmodule
.mclassdefs
do
454 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
459 # Build a new `MModule` from a `node`.
461 # REQUIRE `node.labels.has("MModule")`
462 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
463 var m
= mentities
.get_or_null
(node
.id
.as(Int))
464 if m
isa MModule then return m
466 assert node
.labels
.has
("MModule")
467 var ins
= node
.in_nodes
("DECLARES")
468 var mgroup
: nullable MGroup = null
469 if not ins
.is_empty
then
470 mgroup
= to_mgroup
(model
, ins
.first
)
472 var name
= node
["name"].to_s
473 var location
= to_location
(node
["location"].to_s
)
474 var mmodule
= new MModule(model
, mgroup
, name
, location
)
475 mentities
[node
.id
.as(Int)] = mmodule
476 set_doc
(node
, mmodule
)
477 var imported_mmodules
= new Array[MModule]
478 for smod
in node
.out_nodes
("IMPORTS") do
479 imported_mmodules
.add to_mmodule
(model
, smod
)
481 mmodule
.set_imported_mmodules
(imported_mmodules
)
485 # Build a `NeoNode` representing `mclass`.
486 private fun mclass_node
(mclass
: MClass): NeoNode do
487 var node
= make_node
(mclass
)
488 node
.labels
.add
"MClass"
489 node
["full_name"] = mclass
.full_name
490 node
["kind"] = mclass
.kind
.to_s
491 node
["visibility"] = mclass
.visibility
.to_s
492 if not mclass
.mparameters
.is_empty
then
493 var parameter_names
= new Array[String]
494 for p
in mclass
.mparameters
do parameter_names
.add
(p
.name
)
495 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
497 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
501 # Build a new `MClass` from a `node`.
503 # REQUIRE `node.labels.has("MClass")`
504 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
505 var m
= mentities
.get_or_null
(node
.id
.as(Int))
506 if m
isa MClass then return m
508 assert node
.labels
.has
("MClass")
509 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
510 var name
= node
["name"].to_s
511 var kind
= to_kind
(node
["kind"].to_s
)
512 var visibility
= to_visibility
(node
["visibility"].to_s
)
513 var parameter_names
= new Array[String]
514 if node
.has_key
("parameter_names") then
515 for e
in node
["parameter_names"].as(JsonArray) do
516 parameter_names
.add e
.to_s
519 var mclass
= new MClass(mmodule
, name
, parameter_names
, kind
, visibility
)
520 mentities
[node
.id
.as(Int)] = mclass
521 set_doc
(node
, mclass
)
525 # Build a `NeoNode` representing `mclassdef`.
526 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
527 var node
= make_node
(mclassdef
)
528 node
.labels
.add
"MClassDef"
529 node
["location"] = mclassdef
.location
.to_s
530 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
531 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
532 for mproperty
in mclassdef
.intro_mproperties
do
533 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
535 for mpropdef
in mclassdef
.mpropdefs
do
536 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
538 for sup
in mclassdef
.supertypes
do
539 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
544 # Build a new `MClassDef` from a `node`.
546 # REQUIRE `node.labels.has("MClassDef")`
547 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
548 var m
= mentities
.get_or_null
(node
.id
.as(Int))
549 if m
isa MClassDef then return m
551 assert node
.labels
.has
("MClassDef")
552 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
553 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
554 var location
= to_location
(node
["location"].to_s
)
555 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
556 mentities
[node
.id
.as(Int)] = mclassdef
557 set_doc
(node
, mclassdef
)
558 var supertypes
= new Array[MClassType]
559 for sup
in node
.out_nodes
("INHERITS") do
560 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
562 mclassdef
.set_supertypes
(supertypes
)
563 mclassdef
.add_in_hierarchy
567 # Build a `NeoNode` representing `mproperty`.
568 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
569 var node
= make_node
(mproperty
)
570 node
.labels
.add
"MProperty"
571 node
["full_name"] = mproperty
.full_name
572 node
["visibility"] = mproperty
.visibility
.to_s
573 if mproperty
isa MMethod then
574 node
.labels
.add
"MMethod"
575 node
["is_init"] = mproperty
.is_init
576 else if mproperty
isa MAttribute then
577 node
.labels
.add
"MAttribute"
578 else if mproperty
isa MVirtualTypeProp then
579 node
.labels
.add
"MVirtualTypeProp"
580 else if mproperty
isa MInnerClass then
581 node
.labels
.add
"MInnerClass"
582 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mproperty
.inner
)))
584 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
588 # Build a new `MProperty` from a `node`.
590 # REQUIRE `node.labels.has("MProperty")`
591 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
592 var m
= mentities
.get_or_null
(node
.id
.as(Int))
593 if m
isa MProperty then return m
595 assert node
.labels
.has
("MProperty")
596 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
597 var name
= node
["name"].to_s
598 var visibility
= to_visibility
(node
["visibility"].to_s
)
599 var mprop
: nullable MProperty = null
600 if node
.labels
.has
("MMethod") then
601 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
602 mprop
.is_init
= node
["is_init"].as(Bool)
603 else if node
.labels
.has
("MAttribute") then
604 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
605 else if node
.labels
.has
("MVirtualTypeProp") then
606 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
607 else if node
.labels
.has
("MInnerClass") then
608 var inner
= to_mclass
(model
, node
.out_nodes
("NESTS").first
)
609 mprop
= new MInnerClass(intro_mclassdef
, name
, visibility
, inner
)
611 if mprop
== null then
612 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
615 mentities
[node
.id
.as(Int)] = mprop
620 # Build a `NeoNode` representing `mpropdef`.
621 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
622 var node
= make_node
(mpropdef
)
623 node
.labels
.add
"MPropDef"
624 node
["location"] = mpropdef
.location
.to_s
625 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
626 if mpropdef
isa MMethodDef then
627 node
.labels
.add
"MMethodDef"
628 node
["is_abstract"] = mpropdef
.is_abstract
629 node
["is_intern"] = mpropdef
.is_intern
630 node
["is_extern"] = mpropdef
.is_extern
631 var msignature
= mpropdef
.msignature
632 if msignature
!= null then
633 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
635 else if mpropdef
isa MAttributeDef then
636 node
.labels
.add
"MAttributeDef"
637 var static_mtype
= mpropdef
.static_mtype
638 if static_mtype
!= null then
639 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(static_mtype
)))
641 else if mpropdef
isa MVirtualTypeDef then
642 node
.labels
.add
"MVirtualTypeDef"
643 var bound
= mpropdef
.bound
644 if bound
!= null then
645 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
647 else if mpropdef
isa MInnerClassDef then
648 node
.labels
.add
"MInnerClassDef"
649 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mpropdef
.inner
)))
654 # Build a new `MPropDef` from a `node`.
656 # REQUIRE `node.labels.has("MPropDef")`
657 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
658 var m
= mentities
.get_or_null
(node
.id
.as(Int))
659 if m
isa MPropDef then return m
661 assert node
.labels
.has
("MPropDef")
662 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
663 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
664 var location
= to_location
(node
["location"].to_s
)
665 var mpropdef
: nullable MPropDef = null
666 if node
.labels
.has
("MMethodDef") then
667 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
668 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
669 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
670 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
671 mentities
[node
.id
.as(Int)] = mpropdef
672 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
673 else if node
.labels
.has
("MAttributeDef") then
674 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
675 mentities
[node
.id
.as(Int)] = mpropdef
676 var static_mtype
= node
.out_nodes
("TYPE")
677 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
678 else if node
.labels
.has
("MVirtualTypeDef") then
679 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
680 mentities
[node
.id
.as(Int)] = mpropdef
681 var bound
= node
.out_nodes
("BOUND")
682 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
683 else if node
.labels
.has
("MInnerClassDef") then
684 var inner
= to_mclassdef
(model
, node
.out_nodes
("NESTS").first
)
685 mpropdef
= new MInnerClassDef(mclassdef
,
686 mproperty
.as(MInnerClass), location
, inner
)
687 mentities
[node
.id
.as(Int)] = mpropdef
689 if mpropdef
== null then
690 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
693 set_doc
(node
, mpropdef
)
697 # Build a `NeoNode` representing `mtype`.
698 private fun mtype_node
(mtype
: MType): NeoNode do
699 var node
= make_node
(mtype
)
700 node
.labels
.add
"MType"
701 if mtype
isa MClassType then
702 node
.labels
.add
"MClassType"
703 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
704 for arg
in mtype
.arguments
do
705 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
707 if mtype
isa MGenericType then
708 node
.labels
.add
"MGenericType"
710 else if mtype
isa MVirtualType then
711 node
.labels
.add
"MVirtualType"
712 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
713 else if mtype
isa MParameterType then
714 node
.labels
.add
"MParameterType"
715 node
["rank"] = mtype
.rank
716 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
717 else if mtype
isa MNullableType then
718 node
.labels
.add
"MNullableType"
719 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
720 else if mtype
isa MSignature then
721 node
.labels
.add
"MSignature"
722 var names
= new JsonArray
724 for mparameter
in mtype
.mparameters
do
725 names
.add mparameter
.name
726 var pnode
= mparameter_node
(mparameter
)
728 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
731 if not names
.is_empty
then node
["parameter_names"] = names
732 var return_mtype
= mtype
.return_mtype
733 if return_mtype
!= null then
734 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
736 else if mtype
isa MRawType then
737 node
.labels
.add
"MRawType"
738 var text
= new JsonArray
740 for part
in mtype
.parts
do
742 if part
.target
!= null then
743 var pnode
= mtypepart_node
(part
)
745 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
749 if not text
.is_empty
then node
["text"] = text
754 # Build a `NeoNode` representing `mtypepart`.
755 private fun mtypepart_node
(mtypepart
: MTypePart): NeoNode do
756 var node
= make_node
(mtypepart
)
757 node
.labels
.add
"MTypePart"
758 if mtypepart
.target
!= null then
759 var target_node
= to_node
(mtypepart
.target
.as(not null))
760 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
765 # Build a new `MType` from a `node`.
767 # REQUIRE `node.labels.has("MType")`
768 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
769 var m
= mentities
.get_or_null
(node
.id
.as(Int))
770 if m
isa MType then return m
772 assert node
.labels
.has
("MType")
773 if node
.labels
.has
("MClassType") then
774 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
775 var args
= new Array[MType]
776 for narg
in node
.out_nodes
("ARGUMENT") do
777 args
.add to_mtype
(model
, narg
)
779 var mtype
= mclass
.get_mtype
(args
)
780 mentities
[node
.id
.as(Int)] = mtype
782 else if node
.labels
.has
("MParameterType") then
783 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
784 var rank
= node
["rank"].to_s
.to_i
785 var mtype
= mclass
.mparameters
[rank
]
786 mentities
[node
.id
.as(Int)] = mtype
788 else if node
.labels
.has
("MNullableType") then
789 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
790 var mtype
= intype
.as_nullable
791 mentities
[node
.id
.as(Int)] = mtype
793 else if node
.labels
.has
("MVirtualType") then
794 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
795 assert mproperty
isa MVirtualTypeProp
796 var mtype
= mproperty
.mvirtualtype
797 mentities
[node
.id
.as(Int)] = mtype
799 else if node
.labels
.has
("MSignature") then
800 # Get all param nodes
801 var mparam_nodes
= new HashMap[String, MParameter]
802 for pnode
in node
.out_nodes
("PARAMETER") do
803 var mparam
= to_mparameter
(model
, pnode
)
804 mparam_nodes
[mparam
.name
] = mparam
806 # Load params in the good order
807 var mparam_names
= node
["parameter_names"]
808 var mparameters
= new Array[MParameter]
809 if mparam_names
isa JsonArray then
810 for mparam_name
in mparam_names
do
811 var mparam
= mparam_nodes
[mparam_name
.to_s
]
812 mparameters
.add mparam
815 var return_mtype
: nullable MType = null
816 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
817 if not ret_nodes
.is_empty
then
818 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
820 var mtype
= new MSignature(mparameters
, return_mtype
)
821 mentities
[node
.id
.as(Int)] = mtype
823 else if node
.labels
.has
("MRawType") then
824 var mtype
= new MRawType(model
)
825 var parts
= node
["text"]
826 if parts
isa JsonArray then
828 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
830 for pnode
in node
.out_nodes
("LINK") do
831 assert pnode
.labels
.has
("MTypePart")
832 if not pnode
.out_nodes
("TARGET").is_empty
then
833 var rank
= pnode
["rank"]
834 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
836 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
840 mentities
[node
.id
.as(Int)] = mtype
843 print
"not yet implemented to_mtype for {node.labels.join(",")}"
847 # Build a `NeoNode` representing `mparameter`.
848 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
849 var node
= make_node
(mparameter
)
850 node
.labels
.add
"MParameter"
851 node
["name"] = mparameter
.name
852 node
["is_vararg"] = mparameter
.is_vararg
853 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
857 # Build a new `MParameter` from `node`.
859 # REQUIRE `node.labels.has("MParameter")`
860 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
861 var m
= mentities
.get_or_null
(node
.id
.as(Int))
862 if m
isa MParameter then return m
864 assert node
.labels
.has
("MParameter")
865 var name
= node
["name"].to_s
866 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
867 var is_vararg
= node
["is_vararg"].as(Bool)
868 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
869 mentities
[node
.id
.as(Int)] = mparameter
873 # Get a `Location` from its string representation.
874 private fun to_location
(loc
: String): Location do
876 var parts
= loc
.split_with
(":")
877 var file
= new SourceFile.from_string
(parts
[0], "")
878 var pos
= parts
[1].split_with
("--")
879 var pos1
= pos
[0].split_with
(",")
880 var pos2
= pos
[1].split_with
(",")
881 var line_s
= pos1
[0].to_i
882 var line_e
= pos2
[0].to_i
883 var column_s
= pos1
[1].to_i
885 if pos2
.length
== 2 then pos2
[1].to_i
886 return new Location(file
, line_s
, line_e
, column_s
, column_e
)
889 # Get a `MVisibility` from its string representation.
890 private fun to_visibility
(vis
: String): MVisibility do
891 if vis
== intrude_visibility
.to_s
then
892 return intrude_visibility
893 else if vis
== public_visibility
.to_s
then
894 return public_visibility
895 else if vis
== protected_visibility
.to_s
then
896 return protected_visibility
897 else if vis
== private_visibility
.to_s
then
898 return private_visibility
899 else if vis
== package_visibility
.to_s
then
900 return package_visibility
902 return none_visibility
906 # Get a `MKind` from its string representation.
907 private fun to_kind
(kind
: String): MClassKind do
908 if kind
== abstract_kind
.to_s
then
910 else if kind
== concrete_kind
.to_s
then
912 else if kind
== interface_kind
.to_s
then
913 return interface_kind
914 else if kind
== enum_kind
.to_s
then
916 else if kind
== extern_kind
.to_s
then
919 return raw_kind
(kind
)
923 # Extract the `MDoc` from `node` and link it to `mentity`.
924 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
925 if node
.has_key
("mdoc") then
926 var lines
= new Array[String]
927 for e
in node
["mdoc"].as(JsonArray) do
928 lines
.add e
.to_s
#.replace("\n", "\\n")
931 mdoc
.content
.add_all
(lines
)
932 mdoc
.original_mentity
= mentity