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` or `MVirtualTypeProp`, depending on the class of the represented
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
108 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
109 # `MAttributeDef` or `MVirtualTypeDef`, depending on the class of the
110 # 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.
135 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
136 # `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
137 # class of the represented entity.
139 # Additional label and relationships for `MClassType`:
141 # * If it is a `MGenericType`, also has the `MGenericType` label.
142 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
143 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
145 # Arguments are named following the `parameter_names` attribute of the
146 # `MClass` referred by `CLASS`.
148 # Additional relationship for `MVirtualType`:
150 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
151 # determines the type (usually a `MVirtualTypeProp`).
153 # Additional attribute and relationship for `MParameterType`:
155 # * `rank`: position of the parameter (0 for the first parameter).
156 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
159 # Additional relationship for `MNullableType`:
161 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
163 # Additional attribute and relationship for `MRawType`:
165 # * `text`: JSON array of the parts’ text.
166 # * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
168 # Additional attribute and relationships for `MSignature`:
170 # * `parameter_names`: JSON array representing the list of the parameter names.
171 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
172 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
175 # In order to maintain the correct parameters order, each `MSignature` node
176 # contains an array of parameter names corresponding to the parameter order in
179 # For example, if the source code contains:
181 # fun foo(a: A, b: B, c: C)
183 # The `MSignature` node will contain a property
184 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
185 # with the parameters in the correct order.
189 # * labels: `MParameter`, `model_name` and `MEntity`.
190 # * `is_vararg`: Is the parameter a vararg?
191 # * `rank`: position of the parameter (0 for the first parameter).
192 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
194 # MParameters are also ranked by their position in the corresponding signature.
195 # Rank 0 for the first parameter, 1 for the next one and etc.
199 # * labels: `MTypePart`, `model_name` and `MEntity`.
200 # * `rank`: position in the `MRawType`.
201 # * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
204 import doc
::model_ext
208 # Helper class that can save and load a `Model` into a Neo4j database.
213 # Because we use only one Neo4j instance to store all the models,
214 # we need to mark their appartenance to a particular model and avoid loading all models.
216 # The name is used as a Neo label on each created nodes and used to load nodes from base.
217 var model_name
: String
219 # The toolcontext used to init the `NeoModel` tool.
220 var toolcontext
: ToolContext
222 # The Neo4j `client` used to communicate with the Neo4j instance.
223 var client
: Neo4jClient
225 # Fill `model` using base pointed by `client`.
226 fun load
(model
: Model): Model do
227 var nodes
: Array[NeoNode]
229 toolcontext
.info
("Loading project node...", 1)
230 nodes
= client
.nodes_with_labels
([model_name
, "MProject"])
231 for node
in nodes
do to_mproject
(model
, node
)
232 toolcontext
.info
("Loading groups...", 1)
233 nodes
= client
.nodes_with_labels
([model_name
, "MGroup"])
234 for node
in nodes
do to_mgroup
(model
, node
)
235 toolcontext
.info
("Loading modules...", 1)
236 nodes
= client
.nodes_with_labels
([model_name
, "MModule"])
237 for node
in nodes
do to_mmodule
(model
, node
)
238 toolcontext
.info
("Loading classes...", 1)
239 nodes
= client
.nodes_with_labels
([model_name
, "MClass"])
240 for node
in nodes
do to_mclass
(model
, node
)
241 toolcontext
.info
("Loading class definitions...", 1)
242 nodes
= client
.nodes_with_labels
([model_name
, "MClassDef"])
243 for node
in nodes
do to_mclassdef
(model
, node
)
244 toolcontext
.info
("Loading properties...", 1)
245 nodes
= client
.nodes_with_labels
([model_name
, "MProperty"])
246 for node
in nodes
do to_mproperty
(model
, node
)
247 toolcontext
.info
("Loading property definitions...", 1)
248 nodes
= client
.nodes_with_labels
([model_name
, "MPropDef"])
249 for node
in nodes
do to_mpropdef
(model
, node
)
253 # Save `model` in the base pointed by `client`.
254 fun save
(model
: Model) do
255 var nodes
= collect_model_nodes
(model
)
256 toolcontext
.info
("Save {nodes.length} nodes...", 1)
258 var edges
= collect_model_edges
(model
)
259 toolcontext
.info
("Save {edges.length} edges...", 1)
263 # Save `neo_entities` in base using batch mode.
264 private fun push_all
(neo_entities
: Collection[NeoEntity]) do
265 var batch
= new NeoBatch(client
)
266 var len
= neo_entities
.length
269 for nentity
in neo_entities
do
270 batch
.save_entity
(nentity
)
271 if i
== batch_max_size
then
273 sum
+= batch_max_size
274 toolcontext
.info
(" {sum * 100 / len}% done", 1)
275 batch
= new NeoBatch(client
)
284 # How many operation can be executed in one batch?
285 private var batch_max_size
= 1000
287 # Execute `batch` and check for errors.
289 # Abort if `batch.execute` returns errors.
290 private fun do_batch
(batch
: NeoBatch) do
291 var errors
= batch
.execute
292 if not errors
.is_empty
then
298 # Collect all nodes from the current `model`.
299 private fun collect_model_nodes
(model
: Model): Collection[NeoNode] do
300 for mproject
in model
.mprojects
do
302 for mgroup
in mproject
.mgroups
do to_node
(mgroup
)
307 # Collect all edges from the current `model`.
309 # Actually collect all out_edges from all nodes.
310 private fun collect_model_edges
(model
: Model): Collection[NeoEdge] do
311 var edges
= new HashSet[NeoEdge]
312 for node
in nodes
.values
do edges
.add_all
(node
.out_edges
)
316 # Mentities associated to nodes.
318 # The key is the node’s id.
319 private var mentities
= new HashMap[Int, MEntity]
321 # Nodes associated with MEntities.
322 private var nodes
= new HashMap[MEntity, NeoNode]
324 # Get the `NeoNode` associated with `mentity`.
325 # `mentities` are stored locally to avoid duplication.
326 fun to_node
(mentity
: MEntity): NeoNode do
327 if nodes
.has_key
(mentity
) then return nodes
[mentity
]
328 if mentity
isa MProject then return mproject_node
(mentity
)
329 if mentity
isa MGroup then return mgroup_node
(mentity
)
330 if mentity
isa MModule then return mmodule_node
(mentity
)
331 if mentity
isa MClass then return mclass_node
(mentity
)
332 if mentity
isa MClassDef then return mclassdef_node
(mentity
)
333 if mentity
isa MProperty then return mproperty_node
(mentity
)
334 if mentity
isa MPropDef then return mpropdef_node
(mentity
)
335 if mentity
isa MType then return mtype_node
(mentity
)
336 if mentity
isa MParameter then return mparameter_node
(mentity
)
340 # Get the `MEntity` associated with `node`.
341 fun to_mentity
(model
: Model, node
: NeoNode): MEntity do
342 if node
.labels
.has
("MProject") then return to_mproject
(model
, node
)
343 if node
.labels
.has
("MGroup") then return to_mgroup
(model
, node
)
344 if node
.labels
.has
("MModule") then return to_mmodule
(model
, node
)
345 if node
.labels
.has
("MClass") then return to_mclass
(model
, node
)
346 if node
.labels
.has
("MClassDef") then return to_mclassdef
(model
, node
)
347 if node
.labels
.has
("MProperty") then return to_mproperty
(model
, node
)
348 if node
.labels
.has
("MPropDef") then return to_mpropdef
(model
, node
)
349 if node
.labels
.has
("MType") then return to_mtype
(model
, node
)
350 if node
.labels
.has
("MParameter") then return to_mparameter
(model
, node
)
354 # Make a new `NeoNode` based on `mentity`.
355 private fun make_node
(mentity
: MEntity): NeoNode do
356 var node
= new NeoNode
357 nodes
[mentity
] = node
358 node
.labels
.add
"MEntity"
359 node
.labels
.add model_name
360 node
["name"] = mentity
.name
361 if mentity
.mdoc
!= null then node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
365 # Build a `NeoNode` representing `mproject`.
366 private fun mproject_node
(mproject
: MProject): NeoNode do
367 var node
= make_node
(mproject
)
368 node
.labels
.add
"MProject"
369 var root
= mproject
.root
371 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
376 # Build a new `MProject` from a `node`.
378 # REQUIRE `node.labels.has("MProject")`
379 private fun to_mproject
(model
: Model, node
: NeoNode): MProject do
380 var m
= mentities
.get_or_null
(node
.id
.as(Int))
381 if m
isa MProject then return m
383 assert node
.labels
.has
("MProject")
384 var mproject
= new MProject(node
["name"].to_s
, model
)
385 mentities
[node
.id
.as(Int)] = mproject
386 set_doc
(node
, mproject
)
387 mproject
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
391 # Build a `NeoNode` representing `mgroup`.
392 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
393 var node
= make_node
(mgroup
)
394 node
.labels
.add
"MGroup"
395 node
["full_name"] = mgroup
.full_name
396 var parent
= mgroup
.parent
397 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mproject
)))
398 if parent
!= null then
399 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
401 for mmodule
in mgroup
.mmodules
do
402 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
404 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
405 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
410 # Build a new `MGroup` from a `node`.
412 # REQUIRE `node.labels.has("MGroup")`
413 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
414 var m
= mentities
.get_or_null
(node
.id
.as(Int))
415 if m
isa MGroup then return m
417 assert node
.labels
.has
("MGroup")
418 var mproject
= to_mproject
(model
, node
.out_nodes
("PROJECT").first
)
419 var parent
: nullable MGroup = null
420 var out
= node
.out_nodes
("PARENT")
421 if not out
.is_empty
then
422 parent
= to_mgroup
(model
, out
.first
)
424 var mgroup
= new MGroup(node
["name"].to_s
, mproject
, parent
)
425 mentities
[node
.id
.as(Int)] = mgroup
426 set_doc
(node
, mgroup
)
430 # Build a `NeoNode` representing `mmodule`.
431 private fun mmodule_node
(mmodule
: MModule): NeoNode do
432 var node
= make_node
(mmodule
)
433 node
.labels
.add
"MModule"
434 node
["full_name"] = mmodule
.full_name
435 node
["location"] = mmodule
.location
.to_s
436 for parent
in mmodule
.in_importation
.direct_greaters
do
437 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
439 for mclass
in mmodule
.intro_mclasses
do
440 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
442 for mclassdef
in mmodule
.mclassdefs
do
443 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
448 # Build a new `MModule` from a `node`.
450 # REQUIRE `node.labels.has("MModule")`
451 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
452 var m
= mentities
.get_or_null
(node
.id
.as(Int))
453 if m
isa MModule then return m
455 assert node
.labels
.has
("MModule")
456 var ins
= node
.in_nodes
("DECLARES")
457 var mgroup
: nullable MGroup = null
458 if not ins
.is_empty
then
459 mgroup
= to_mgroup
(model
, ins
.first
)
461 var name
= node
["name"].to_s
462 var location
= to_location
(node
["location"].to_s
)
463 var mmodule
= new MModule(model
, mgroup
, name
, location
)
464 mentities
[node
.id
.as(Int)] = mmodule
465 set_doc
(node
, mmodule
)
466 var imported_mmodules
= new Array[MModule]
467 for smod
in node
.out_nodes
("IMPORTS") do
468 imported_mmodules
.add to_mmodule
(model
, smod
)
470 mmodule
.set_imported_mmodules
(imported_mmodules
)
474 # Build a `NeoNode` representing `mclass`.
475 private fun mclass_node
(mclass
: MClass): NeoNode do
476 var node
= make_node
(mclass
)
477 node
.labels
.add
"MClass"
478 node
["full_name"] = mclass
.full_name
479 node
["kind"] = mclass
.kind
.to_s
480 node
["visibility"] = mclass
.visibility
.to_s
481 if not mclass
.mparameters
.is_empty
then
482 var parameter_names
= new Array[String]
483 for p
in mclass
.mparameters
do parameter_names
.add
(p
.name
)
484 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
486 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
490 # Build a new `MClass` from a `node`.
492 # REQUIRE `node.labels.has("MClass")`
493 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
494 var m
= mentities
.get_or_null
(node
.id
.as(Int))
495 if m
isa MClass then return m
497 assert node
.labels
.has
("MClass")
498 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
499 var name
= node
["name"].to_s
500 var kind
= to_kind
(node
["kind"].to_s
)
501 var visibility
= to_visibility
(node
["visibility"].to_s
)
502 var parameter_names
= new Array[String]
503 if node
.has_key
("parameter_names") then
504 for e
in node
["parameter_names"].as(JsonArray) do
505 parameter_names
.add e
.to_s
508 var mclass
= new MClass(mmodule
, name
, parameter_names
, kind
, visibility
)
509 mentities
[node
.id
.as(Int)] = mclass
510 set_doc
(node
, mclass
)
514 # Build a `NeoNode` representing `mclassdef`.
515 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
516 var node
= make_node
(mclassdef
)
517 node
.labels
.add
"MClassDef"
518 node
["location"] = mclassdef
.location
.to_s
519 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
520 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
521 for mproperty
in mclassdef
.intro_mproperties
do
522 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
524 for mpropdef
in mclassdef
.mpropdefs
do
525 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
527 for sup
in mclassdef
.supertypes
do
528 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
533 # Build a new `MClassDef` from a `node`.
535 # REQUIRE `node.labels.has("MClassDef")`
536 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
537 var m
= mentities
.get_or_null
(node
.id
.as(Int))
538 if m
isa MClassDef then return m
540 assert node
.labels
.has
("MClassDef")
541 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
542 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
543 var location
= to_location
(node
["location"].to_s
)
544 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
545 mentities
[node
.id
.as(Int)] = mclassdef
546 set_doc
(node
, mclassdef
)
547 var supertypes
= new Array[MClassType]
548 for sup
in node
.out_nodes
("INHERITS") do
549 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
551 mclassdef
.set_supertypes
(supertypes
)
552 mclassdef
.add_in_hierarchy
556 # Build a `NeoNode` representing `mproperty`.
557 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
558 var node
= make_node
(mproperty
)
559 node
.labels
.add
"MProperty"
560 node
["full_name"] = mproperty
.full_name
561 node
["visibility"] = mproperty
.visibility
.to_s
562 if mproperty
isa MMethod then
563 node
.labels
.add
"MMethod"
564 node
["is_init"] = mproperty
.is_init
565 else if mproperty
isa MAttribute then
566 node
.labels
.add
"MAttribute"
567 else if mproperty
isa MVirtualTypeProp then
568 node
.labels
.add
"MVirtualTypeProp"
570 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
574 # Build a new `MProperty` from a `node`.
576 # REQUIRE `node.labels.has("MProperty")`
577 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
578 var m
= mentities
.get_or_null
(node
.id
.as(Int))
579 if m
isa MProperty then return m
581 assert node
.labels
.has
("MProperty")
582 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
583 var name
= node
["name"].to_s
584 var visibility
= to_visibility
(node
["visibility"].to_s
)
585 var mprop
: nullable MProperty = null
586 if node
.labels
.has
("MMethod") then
587 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
588 mprop
.is_init
= node
["is_init"].as(Bool)
589 else if node
.labels
.has
("MAttribute") then
590 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
591 else if node
.labels
.has
("MVirtualTypeProp") then
592 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
594 if mprop
== null then
595 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
598 mentities
[node
.id
.as(Int)] = mprop
603 # Build a `NeoNode` representing `mpropdef`.
604 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
605 var node
= make_node
(mpropdef
)
606 node
.labels
.add
"MPropDef"
607 node
["location"] = mpropdef
.location
.to_s
608 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
609 if mpropdef
isa MMethodDef then
610 node
.labels
.add
"MMethodDef"
611 node
["is_abstract"] = mpropdef
.is_abstract
612 node
["is_intern"] = mpropdef
.is_intern
613 node
["is_extern"] = mpropdef
.is_extern
614 var msignature
= mpropdef
.msignature
615 if msignature
!= null then
616 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
618 else if mpropdef
isa MAttributeDef then
619 node
.labels
.add
"MAttributeDef"
620 var static_mtype
= mpropdef
.static_mtype
621 if static_mtype
!= null then
622 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(static_mtype
)))
624 else if mpropdef
isa MVirtualTypeDef then
625 node
.labels
.add
"MVirtualTypeDef"
626 var bound
= mpropdef
.bound
627 if bound
!= null then
628 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
634 # Build a new `MPropDef` from a `node`.
636 # REQUIRE `node.labels.has("MPropDef")`
637 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
638 var m
= mentities
.get_or_null
(node
.id
.as(Int))
639 if m
isa MPropDef then return m
641 assert node
.labels
.has
("MPropDef")
642 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
643 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
644 var location
= to_location
(node
["location"].to_s
)
645 var mpropdef
: nullable MPropDef = null
646 if node
.labels
.has
("MMethodDef") then
647 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
648 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
649 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
650 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
651 mentities
[node
.id
.as(Int)] = mpropdef
652 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
653 else if node
.labels
.has
("MAttributeDef") then
654 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
655 mentities
[node
.id
.as(Int)] = mpropdef
656 var static_mtype
= node
.out_nodes
("TYPE")
657 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
658 else if node
.labels
.has
("MVirtualTypeDef") then
659 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
660 mentities
[node
.id
.as(Int)] = mpropdef
661 var bound
= node
.out_nodes
("BOUND")
662 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
664 if mpropdef
== null then
665 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
668 set_doc
(node
, mpropdef
)
672 # Build a `NeoNode` representing `mtype`.
673 private fun mtype_node
(mtype
: MType): NeoNode do
674 var node
= make_node
(mtype
)
675 node
.labels
.add
"MType"
676 if mtype
isa MClassType then
677 node
.labels
.add
"MClassType"
678 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
679 for arg
in mtype
.arguments
do
680 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
682 if mtype
isa MGenericType then
683 node
.labels
.add
"MGenericType"
685 else if mtype
isa MVirtualType then
686 node
.labels
.add
"MVirtualType"
687 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
688 else if mtype
isa MParameterType then
689 node
.labels
.add
"MParameterType"
690 node
["rank"] = mtype
.rank
691 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
692 else if mtype
isa MNullableType then
693 node
.labels
.add
"MNullableType"
694 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
695 else if mtype
isa MSignature then
696 node
.labels
.add
"MSignature"
697 var names
= new JsonArray
699 for mparameter
in mtype
.mparameters
do
700 names
.add mparameter
.name
701 var pnode
= mparameter_node
(mparameter
)
703 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
706 if not names
.is_empty
then node
["parameter_names"] = names
707 var return_mtype
= mtype
.return_mtype
708 if return_mtype
!= null then
709 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
711 else if mtype
isa MRawType then
712 node
.labels
.add
"MRawType"
713 var text
= new JsonArray
715 for part
in mtype
.parts
do
717 if part
.target
!= null then
718 var pnode
= mtypepart_node
(part
)
720 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
724 if not text
.is_empty
then node
["text"] = text
729 # Build a `NeoNode` representing `mtypepart`.
730 private fun mtypepart_node
(mtypepart
: MTypePart): NeoNode do
731 var node
= make_node
(mtypepart
)
732 node
.labels
.add
"MTypePart"
733 if mtypepart
.target
!= null then
734 var target_node
= to_node
(mtypepart
.target
.as(not null))
735 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
740 # Build a new `MType` from a `node`.
742 # REQUIRE `node.labels.has("MType")`
743 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
744 var m
= mentities
.get_or_null
(node
.id
.as(Int))
745 if m
isa MType then return m
747 assert node
.labels
.has
("MType")
748 if node
.labels
.has
("MClassType") then
749 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
750 var args
= new Array[MType]
751 for narg
in node
.out_nodes
("ARGUMENT") do
752 args
.add to_mtype
(model
, narg
)
754 var mtype
= mclass
.get_mtype
(args
)
755 mentities
[node
.id
.as(Int)] = mtype
757 else if node
.labels
.has
("MParameterType") then
758 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
759 var rank
= node
["rank"].to_s
.to_i
760 var mtype
= mclass
.mparameters
[rank
]
761 mentities
[node
.id
.as(Int)] = mtype
763 else if node
.labels
.has
("MNullableType") then
764 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
765 var mtype
= intype
.as_nullable
766 mentities
[node
.id
.as(Int)] = mtype
768 else if node
.labels
.has
("MVirtualType") then
769 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
770 assert mproperty
isa MVirtualTypeProp
771 var mtype
= mproperty
.mvirtualtype
772 mentities
[node
.id
.as(Int)] = mtype
774 else if node
.labels
.has
("MSignature") then
775 # Get all param nodes
776 var mparam_nodes
= new HashMap[String, MParameter]
777 for pnode
in node
.out_nodes
("PARAMETER") do
778 var mparam
= to_mparameter
(model
, pnode
)
779 mparam_nodes
[mparam
.name
] = mparam
781 # Load params in the good order
782 var mparam_names
= node
["parameter_names"]
783 var mparameters
= new Array[MParameter]
784 if mparam_names
isa JsonArray then
785 for mparam_name
in mparam_names
do
786 var mparam
= mparam_nodes
[mparam_name
.to_s
]
787 mparameters
.add mparam
790 var return_mtype
: nullable MType = null
791 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
792 if not ret_nodes
.is_empty
then
793 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
795 var mtype
= new MSignature(mparameters
, return_mtype
)
796 mentities
[node
.id
.as(Int)] = mtype
798 else if node
.labels
.has
("MRawType") then
799 var mtype
= new MRawType(model
)
800 var parts
= node
["text"]
801 if parts
isa JsonArray then
803 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
805 for pnode
in node
.out_nodes
("LINK") do
806 assert pnode
.labels
.has
("MTypePart")
807 if not pnode
.out_nodes
("TARGET").is_empty
then
808 var rank
= pnode
["rank"]
809 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
811 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
815 mentities
[node
.id
.as(Int)] = mtype
818 print
"not yet implemented to_mtype for {node.labels.join(",")}"
822 # Build a `NeoNode` representing `mparameter`.
823 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
824 var node
= make_node
(mparameter
)
825 node
.labels
.add
"MParameter"
826 node
["name"] = mparameter
.name
827 node
["is_vararg"] = mparameter
.is_vararg
828 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
832 # Build a new `MParameter` from `node`.
834 # REQUIRE `node.labels.has("MParameter")`
835 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
836 var m
= mentities
.get_or_null
(node
.id
.as(Int))
837 if m
isa MParameter then return m
839 assert node
.labels
.has
("MParameter")
840 var name
= node
["name"].to_s
841 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
842 var is_vararg
= node
["is_vararg"].as(Bool)
843 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
844 mentities
[node
.id
.as(Int)] = mparameter
848 # Get a `Location` from its string representation.
849 private fun to_location
(loc
: String): Location do
851 var parts
= loc
.split_with
(":")
852 var file
= new SourceFile.from_string
(parts
[0], "")
853 var pos
= parts
[1].split_with
("--")
854 var pos1
= pos
[0].split_with
(",")
855 var pos2
= pos
[1].split_with
(",")
856 var line_s
= pos1
[0].to_i
857 var line_e
= pos2
[0].to_i
858 var column_s
= pos1
[1].to_i
860 if pos2
.length
== 2 then pos2
[1].to_i
861 return new Location(file
, line_s
, line_e
, column_s
, column_e
)
864 # Get a `MVisibility` from its string representation.
865 private fun to_visibility
(vis
: String): MVisibility do
866 if vis
== intrude_visibility
.to_s
then
867 return intrude_visibility
868 else if vis
== public_visibility
.to_s
then
869 return public_visibility
870 else if vis
== protected_visibility
.to_s
then
871 return protected_visibility
872 else if vis
== private_visibility
.to_s
then
873 return private_visibility
874 else if vis
== package_visibility
.to_s
then
875 return package_visibility
877 return none_visibility
881 # Get a `MKind` from its string representation.
882 private fun to_kind
(kind
: String): MClassKind do
883 if kind
== abstract_kind
.to_s
then
885 else if kind
== concrete_kind
.to_s
then
887 else if kind
== interface_kind
.to_s
then
888 return interface_kind
889 else if kind
== enum_kind
.to_s
then
891 else if kind
== extern_kind
.to_s
then
894 return raw_kind
(kind
)
898 # Extract the `MDoc` from `node` and link it to `mentity`.
899 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
900 if node
.has_key
("mdoc") then
901 var lines
= new Array[String]
902 for e
in node
["mdoc"].as(JsonArray) do
903 lines
.add e
.to_s
#.replace("\n", "\\n")
906 mdoc
.content
.add_all
(lines
)
907 mdoc
.original_mentity
= mentity