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 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
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 project node...", 1)
237 nodes
= client
.nodes_with_labels
([model_name
, "MProject"])
238 for node
in nodes
do to_mproject
(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 mproject
in model
.mprojects
do
309 for mgroup
in mproject
.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 MProject then return mproject_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
("MProject") then return to_mproject
(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 node
["mdoc"] = new JsonArray.from
(mentity
.mdoc
.content
)
372 # Build a `NeoNode` representing `mproject`.
373 private fun mproject_node
(mproject
: MProject): NeoNode do
374 var node
= make_node
(mproject
)
375 node
.labels
.add
"MProject"
376 var root
= mproject
.root
378 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", to_node
(root
)))
383 # Build a new `MProject` from a `node`.
385 # REQUIRE `node.labels.has("MProject")`
386 private fun to_mproject
(model
: Model, node
: NeoNode): MProject do
387 var m
= mentities
.get_or_null
(node
.id
.as(Int))
388 if m
isa MProject then return m
390 assert node
.labels
.has
("MProject")
391 var mproject
= new MProject(node
["name"].to_s
, model
)
392 mentities
[node
.id
.as(Int)] = mproject
393 set_doc
(node
, mproject
)
394 mproject
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
398 # Build a `NeoNode` representing `mgroup`.
399 private fun mgroup_node
(mgroup
: MGroup): NeoNode do
400 var node
= make_node
(mgroup
)
401 node
.labels
.add
"MGroup"
402 var parent
= mgroup
.parent
403 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", to_node
(mgroup
.mproject
)))
404 if parent
!= null then
405 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", to_node
(parent
)))
407 for mmodule
in mgroup
.mmodules
do
408 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mmodule
)))
410 for subgroup
in mgroup
.in_nesting
.direct_smallers
do
411 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(subgroup
)))
416 # Build a new `MGroup` from a `node`.
418 # REQUIRE `node.labels.has("MGroup")`
419 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
420 var m
= mentities
.get_or_null
(node
.id
.as(Int))
421 if m
isa MGroup then return m
423 assert node
.labels
.has
("MGroup")
424 var mproject
= to_mproject
(model
, node
.out_nodes
("PROJECT").first
)
425 var parent
: nullable MGroup = null
426 var out
= node
.out_nodes
("PARENT")
427 if not out
.is_empty
then
428 parent
= to_mgroup
(model
, out
.first
)
430 var mgroup
= new MGroup(node
["name"].to_s
, mproject
, parent
)
431 mentities
[node
.id
.as(Int)] = mgroup
432 set_doc
(node
, mgroup
)
436 # Build a `NeoNode` representing `mmodule`.
437 private fun mmodule_node
(mmodule
: MModule): NeoNode do
438 var node
= make_node
(mmodule
)
439 node
.labels
.add
"MModule"
440 node
["location"] = mmodule
.location
.to_s
441 for parent
in mmodule
.in_importation
.direct_greaters
do
442 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", to_node
(parent
)))
444 for mclass
in mmodule
.intro_mclasses
do
445 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mclass
)))
447 for mclassdef
in mmodule
.mclassdefs
do
448 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mclassdef
)))
453 # Build a new `MModule` from a `node`.
455 # REQUIRE `node.labels.has("MModule")`
456 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
457 var m
= mentities
.get_or_null
(node
.id
.as(Int))
458 if m
isa MModule then return m
460 assert node
.labels
.has
("MModule")
461 var ins
= node
.in_nodes
("DECLARES")
462 var mgroup
: nullable MGroup = null
463 if not ins
.is_empty
then
464 mgroup
= to_mgroup
(model
, ins
.first
)
466 var name
= node
["name"].to_s
467 var location
= to_location
(node
["location"].to_s
)
468 var mmodule
= new MModule(model
, mgroup
, name
, location
)
469 mentities
[node
.id
.as(Int)] = mmodule
470 set_doc
(node
, mmodule
)
471 var imported_mmodules
= new Array[MModule]
472 for smod
in node
.out_nodes
("IMPORTS") do
473 imported_mmodules
.add to_mmodule
(model
, smod
)
475 mmodule
.set_imported_mmodules
(imported_mmodules
)
479 # Build a `NeoNode` representing `mclass`.
480 private fun mclass_node
(mclass
: MClass): NeoNode do
481 var node
= make_node
(mclass
)
482 node
.labels
.add
"MClass"
483 node
["kind"] = mclass
.kind
.to_s
484 node
["visibility"] = mclass
.visibility
.to_s
485 if not mclass
.mparameters
.is_empty
then
486 var parameter_names
= new Array[String]
487 for p
in mclass
.mparameters
do parameter_names
.add
(p
.name
)
488 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
490 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", to_node
(mclass
.mclass_type
)))
494 # Build a new `MClass` from a `node`.
496 # REQUIRE `node.labels.has("MClass")`
497 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
498 var m
= mentities
.get_or_null
(node
.id
.as(Int))
499 if m
isa MClass then return m
501 assert node
.labels
.has
("MClass")
502 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
503 var name
= node
["name"].to_s
504 var kind
= to_kind
(node
["kind"].to_s
)
505 var visibility
= to_visibility
(node
["visibility"].to_s
)
506 var parameter_names
= new Array[String]
507 if node
.has_key
("parameter_names") then
508 for e
in node
["parameter_names"].as(JsonArray) do
509 parameter_names
.add e
.to_s
512 var mclass
= new MClass(mmodule
, name
, parameter_names
, kind
, visibility
)
513 mentities
[node
.id
.as(Int)] = mclass
514 set_doc
(node
, mclass
)
518 # Build a `NeoNode` representing `mclassdef`.
519 private fun mclassdef_node
(mclassdef
: MClassDef): NeoNode do
520 var node
= make_node
(mclassdef
)
521 node
.labels
.add
"MClassDef"
522 node
["location"] = mclassdef
.location
.to_s
523 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", to_node
(mclassdef
.bound_mtype
)))
524 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", to_node
(mclassdef
.mclass
)))
525 for mproperty
in mclassdef
.intro_mproperties
do
526 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", to_node
(mproperty
)))
528 for mpropdef
in mclassdef
.mpropdefs
do
529 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", to_node
(mpropdef
)))
531 for sup
in mclassdef
.supertypes
do
532 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", to_node
(sup
)))
537 # Build a new `MClassDef` from a `node`.
539 # REQUIRE `node.labels.has("MClassDef")`
540 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
541 var m
= mentities
.get_or_null
(node
.id
.as(Int))
542 if m
isa MClassDef then return m
544 assert node
.labels
.has
("MClassDef")
545 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
546 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
547 var location
= to_location
(node
["location"].to_s
)
548 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
549 mentities
[node
.id
.as(Int)] = mclassdef
550 set_doc
(node
, mclassdef
)
551 var supertypes
= new Array[MClassType]
552 for sup
in node
.out_nodes
("INHERITS") do
553 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
555 mclassdef
.set_supertypes
(supertypes
)
556 mclassdef
.add_in_hierarchy
560 # Build a `NeoNode` representing `mproperty`.
561 private fun mproperty_node
(mproperty
: MProperty): NeoNode do
562 var node
= make_node
(mproperty
)
563 node
.labels
.add
"MProperty"
564 node
["visibility"] = mproperty
.visibility
.to_s
565 if mproperty
isa MMethod then
566 node
.labels
.add
"MMethod"
567 node
["is_init"] = mproperty
.is_init
568 else if mproperty
isa MAttribute then
569 node
.labels
.add
"MAttribute"
570 else if mproperty
isa MVirtualTypeProp then
571 node
.labels
.add
"MVirtualTypeProp"
572 else if mproperty
isa MInnerClass then
573 node
.labels
.add
"MInnerClass"
574 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mproperty
.inner
)))
576 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", to_node
(mproperty
.intro_mclassdef
)))
580 # Build a new `MProperty` from a `node`.
582 # REQUIRE `node.labels.has("MProperty")`
583 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
584 var m
= mentities
.get_or_null
(node
.id
.as(Int))
585 if m
isa MProperty then return m
587 assert node
.labels
.has
("MProperty")
588 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
589 var name
= node
["name"].to_s
590 var visibility
= to_visibility
(node
["visibility"].to_s
)
591 var mprop
: nullable MProperty = null
592 if node
.labels
.has
("MMethod") then
593 mprop
= new MMethod(intro_mclassdef
, name
, visibility
)
594 mprop
.is_init
= node
["is_init"].as(Bool)
595 else if node
.labels
.has
("MAttribute") then
596 mprop
= new MAttribute(intro_mclassdef
, name
, visibility
)
597 else if node
.labels
.has
("MVirtualTypeProp") then
598 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, visibility
)
599 else if node
.labels
.has
("MInnerClass") then
600 var inner
= to_mclass
(model
, node
.out_nodes
("NESTS").first
)
601 mprop
= new MInnerClass(intro_mclassdef
, name
, visibility
, inner
)
603 if mprop
== null then
604 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
607 mentities
[node
.id
.as(Int)] = mprop
612 # Build a `NeoNode` representing `mpropdef`.
613 private fun mpropdef_node
(mpropdef
: MPropDef): NeoNode do
614 var node
= make_node
(mpropdef
)
615 node
.labels
.add
"MPropDef"
616 node
["location"] = mpropdef
.location
.to_s
617 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", to_node
(mpropdef
.mproperty
)))
618 if mpropdef
isa MMethodDef then
619 node
.labels
.add
"MMethodDef"
620 node
["is_abstract"] = mpropdef
.is_abstract
621 node
["is_intern"] = mpropdef
.is_intern
622 node
["is_extern"] = mpropdef
.is_extern
623 var msignature
= mpropdef
.msignature
624 if msignature
!= null then
625 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", to_node
(msignature
)))
627 else if mpropdef
isa MAttributeDef then
628 node
.labels
.add
"MAttributeDef"
629 var static_mtype
= mpropdef
.static_mtype
630 if static_mtype
!= null then
631 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(static_mtype
)))
633 else if mpropdef
isa MVirtualTypeDef then
634 node
.labels
.add
"MVirtualTypeDef"
635 var bound
= mpropdef
.bound
636 if bound
!= null then
637 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", to_node
(bound
)))
639 else if mpropdef
isa MInnerClassDef then
640 node
.labels
.add
"MInnerClassDef"
641 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", to_node
(mpropdef
.inner
)))
646 # Build a new `MPropDef` from a `node`.
648 # REQUIRE `node.labels.has("MPropDef")`
649 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
650 var m
= mentities
.get_or_null
(node
.id
.as(Int))
651 if m
isa MPropDef then return m
653 assert node
.labels
.has
("MPropDef")
654 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
655 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
656 var location
= to_location
(node
["location"].to_s
)
657 var mpropdef
: nullable MPropDef = null
658 if node
.labels
.has
("MMethodDef") then
659 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
660 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
661 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
662 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
663 mentities
[node
.id
.as(Int)] = mpropdef
664 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
665 else if node
.labels
.has
("MAttributeDef") then
666 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
667 mentities
[node
.id
.as(Int)] = mpropdef
668 var static_mtype
= node
.out_nodes
("TYPE")
669 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
670 else if node
.labels
.has
("MVirtualTypeDef") then
671 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
672 mentities
[node
.id
.as(Int)] = mpropdef
673 var bound
= node
.out_nodes
("BOUND")
674 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
675 else if node
.labels
.has
("MInnerClassDef") then
676 var inner
= to_mclassdef
(model
, node
.out_nodes
("NESTS").first
)
677 mpropdef
= new MInnerClassDef(mclassdef
,
678 mproperty
.as(MInnerClass), location
, inner
)
679 mentities
[node
.id
.as(Int)] = mpropdef
681 if mpropdef
== null then
682 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
685 set_doc
(node
, mpropdef
)
689 # Build a `NeoNode` representing `mtype`.
690 private fun mtype_node
(mtype
: MType): NeoNode do
691 var node
= make_node
(mtype
)
692 node
.labels
.add
"MType"
693 if mtype
isa MClassType then
694 node
.labels
.add
"MClassType"
695 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
696 for arg
in mtype
.arguments
do
697 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", to_node
(arg
)))
699 if mtype
isa MGenericType then
700 node
.labels
.add
"MGenericType"
702 else if mtype
isa MVirtualType then
703 node
.labels
.add
"MVirtualType"
704 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", to_node
(mtype
.mproperty
)))
705 else if mtype
isa MParameterType then
706 node
.labels
.add
"MParameterType"
707 node
["rank"] = mtype
.rank
708 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", to_node
(mtype
.mclass
)))
709 else if mtype
isa MNullableType then
710 node
.labels
.add
"MNullableType"
711 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mtype
.mtype
)))
712 else if mtype
isa MSignature then
713 node
.labels
.add
"MSignature"
714 var names
= new JsonArray
716 for mparameter
in mtype
.mparameters
do
717 names
.add mparameter
.name
718 var pnode
= mparameter_node
(mparameter
)
720 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
723 if not names
.is_empty
then node
["parameter_names"] = names
724 var return_mtype
= mtype
.return_mtype
725 if return_mtype
!= null then
726 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", to_node
(return_mtype
)))
728 else if mtype
isa MRawType then
729 node
.labels
.add
"MRawType"
730 var text
= new JsonArray
732 for part
in mtype
.parts
do
734 if part
.target
!= null then
735 var pnode
= mtypepart_node
(part
)
737 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
741 if not text
.is_empty
then node
["text"] = text
746 # Build a `NeoNode` representing `mtypepart`.
747 private fun mtypepart_node
(mtypepart
: MTypePart): NeoNode do
748 var node
= make_node
(mtypepart
)
749 node
.labels
.add
"MTypePart"
750 if mtypepart
.target
!= null then
751 var target_node
= to_node
(mtypepart
.target
.as(not null))
752 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
757 # Build a new `MType` from a `node`.
759 # REQUIRE `node.labels.has("MType")`
760 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
761 var m
= mentities
.get_or_null
(node
.id
.as(Int))
762 if m
isa MType then return m
764 assert node
.labels
.has
("MType")
765 if node
.labels
.has
("MClassType") then
766 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
767 var args
= new Array[MType]
768 for narg
in node
.out_nodes
("ARGUMENT") do
769 args
.add to_mtype
(model
, narg
)
771 var mtype
= mclass
.get_mtype
(args
)
772 mentities
[node
.id
.as(Int)] = mtype
774 else if node
.labels
.has
("MParameterType") then
775 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
776 var rank
= node
["rank"].to_s
.to_i
777 var mtype
= mclass
.mparameters
[rank
]
778 mentities
[node
.id
.as(Int)] = mtype
780 else if node
.labels
.has
("MNullableType") then
781 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
782 var mtype
= intype
.as_nullable
783 mentities
[node
.id
.as(Int)] = mtype
785 else if node
.labels
.has
("MVirtualType") then
786 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
787 assert mproperty
isa MVirtualTypeProp
788 var mtype
= mproperty
.mvirtualtype
789 mentities
[node
.id
.as(Int)] = mtype
791 else if node
.labels
.has
("MSignature") then
792 # Get all param nodes
793 var mparam_nodes
= new HashMap[String, MParameter]
794 for pnode
in node
.out_nodes
("PARAMETER") do
795 var mparam
= to_mparameter
(model
, pnode
)
796 mparam_nodes
[mparam
.name
] = mparam
798 # Load params in the good order
799 var mparam_names
= node
["parameter_names"]
800 var mparameters
= new Array[MParameter]
801 if mparam_names
isa JsonArray then
802 for mparam_name
in mparam_names
do
803 var mparam
= mparam_nodes
[mparam_name
.to_s
]
804 mparameters
.add mparam
807 var return_mtype
: nullable MType = null
808 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
809 if not ret_nodes
.is_empty
then
810 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
812 var mtype
= new MSignature(mparameters
, return_mtype
)
813 mentities
[node
.id
.as(Int)] = mtype
815 else if node
.labels
.has
("MRawType") then
816 var mtype
= new MRawType(model
)
817 var parts
= node
["text"]
818 if parts
isa JsonArray then
820 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
822 for pnode
in node
.out_nodes
("LINK") do
823 assert pnode
.labels
.has
("MTypePart")
824 if not pnode
.out_nodes
("TARGET").is_empty
then
825 var rank
= pnode
["rank"]
826 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
828 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
832 mentities
[node
.id
.as(Int)] = mtype
835 print
"not yet implemented to_mtype for {node.labels.join(",")}"
839 # Build a `NeoNode` representing `mparameter`.
840 private fun mparameter_node
(mparameter
: MParameter): NeoNode do
841 var node
= make_node
(mparameter
)
842 node
.labels
.add
"MParameter"
843 node
["name"] = mparameter
.name
844 node
["is_vararg"] = mparameter
.is_vararg
845 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", to_node
(mparameter
.mtype
)))
849 # Build a new `MParameter` from `node`.
851 # REQUIRE `node.labels.has("MParameter")`
852 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
853 var m
= mentities
.get_or_null
(node
.id
.as(Int))
854 if m
isa MParameter then return m
856 assert node
.labels
.has
("MParameter")
857 var name
= node
["name"].to_s
858 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
859 var is_vararg
= node
["is_vararg"].as(Bool)
860 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
861 mentities
[node
.id
.as(Int)] = mparameter
865 # Get a `Location` from its string representation.
866 private fun to_location
(loc
: String): Location do
868 var parts
= loc
.split_with
(":")
869 var file
= new SourceFile.from_string
(parts
[0], "")
870 var pos
= parts
[1].split_with
("--")
871 var pos1
= pos
[0].split_with
(",")
872 var pos2
= pos
[1].split_with
(",")
873 var line_s
= pos1
[0].to_i
874 var line_e
= pos2
[0].to_i
875 var column_s
= pos1
[1].to_i
877 if pos2
.length
== 2 then pos2
[1].to_i
878 return new Location(file
, line_s
, line_e
, column_s
, column_e
)
881 # Get a `MVisibility` from its string representation.
882 private fun to_visibility
(vis
: String): MVisibility do
883 if vis
== intrude_visibility
.to_s
then
884 return intrude_visibility
885 else if vis
== public_visibility
.to_s
then
886 return public_visibility
887 else if vis
== protected_visibility
.to_s
then
888 return protected_visibility
889 else if vis
== private_visibility
.to_s
then
890 return private_visibility
891 else if vis
== package_visibility
.to_s
then
892 return package_visibility
894 return none_visibility
898 # Get a `MKind` from its string representation.
899 private fun to_kind
(kind
: String): MClassKind do
900 if kind
== abstract_kind
.to_s
then
902 else if kind
== concrete_kind
.to_s
then
904 else if kind
== interface_kind
.to_s
then
905 return interface_kind
906 else if kind
== enum_kind
.to_s
then
908 else if kind
== extern_kind
.to_s
then
911 return raw_kind
(kind
)
915 # Extract the `MDoc` from `node` and link it to `mentity`.
916 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
917 if node
.has_key
("mdoc") then
918 var lines
= new Array[String]
919 for e
in node
["mdoc"].as(JsonArray) do
920 lines
.add e
.to_s
#.replace("\n", "\\n")
923 mdoc
.content
.add_all
(lines
)
924 mdoc
.original_mentity
= mentity