1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Save and load a `Model` to/from a Neo4j graph.
17 # Nit models are composed by MEntities.
18 # This module creates NeoNode for each MEntity found in a `Model` and save them
19 # into Neo4j database.
23 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
25 # Structure of the nit `Model` in the graph:
27 # Note : Any null or empty attribute will not be saved in the database.
29 # For any `MEntity` (in addition to specific data):
31 # * labels: model name (`model_name`) and `MEntity`.
32 # * `name`: short (unqualified) name.
33 # * `mdoc`: JSON array representing the associated Markdown documentation
36 # Note : All nodes described here are MEntities.
40 # * labels: `MPackage`, `model_name` and `MEntity`.
41 # * `(:MPackage)-[:ROOT]->(:MGroup)`: root of the group tree.
45 # * labels: `MGroup`, `model_name` and `MEntity`.
46 # * `(:MGroup)-[:PROJECT]->(:MPackage)`: associated package.
47 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
49 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
51 # * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
56 # * labels: `MModule`, `model_name` and `MEntity`.
57 # * `location`: origin of the definition. SEE: `Location.to_s`
58 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
59 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
61 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
66 # * labels: `MClass`, `model_name` and `MEntity`.
67 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
68 # * `visibility`: visibility of the class.
69 # * `parameter_names`: JSON array listing the name of each formal generic
70 # parameter (in order of declaration).
71 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
73 # Arguments in the `CLASSTYPE` are named following the `parameter_names`
74 # attribute of the `MClassDef` that introduces the class. A class definition
75 # introduces a class if and only if it has this class as `MCLASS` and
76 # has `is_intro` set to `true`.
80 # * labels: `MClassDef`, `model_name` and `MEntity`.
81 # * `location`: origin of the definition. SEE: `Location.to_s`
82 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
84 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
85 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
87 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
88 # classdef (introductions and redefinitions).
89 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
93 # * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
94 # `MAttribute` `MVirtualTypeProp` or `MInnerClass`, depending on the class of
95 # the represented entity.
96 # * `visibility`: visibility of the property.
97 # * `is_init`: Indicates if the property is a constructor. Exists only if the
98 # node is a `MMethod`.
99 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
102 # Additional relationship for `MInnerClass`:
104 # * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
108 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
109 # `MAttributeDef`, `MVirtualTypeDef` or `MInnerClassDef`, depending on the
110 # class of the represented entity.
111 # * `location`: origin of the definition. SEE: `Location.to_s`.
112 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
114 # Additional attributes and relationship for `MMethodDef`:
116 # * `is_abstract`: Is the method definition abstract?
117 # * `is_intern`: Is the method definition intern?
118 # * `is_extern`: Is the method definition extern?
119 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
120 # property definition.
122 # Additional relationship for `MAttributeDef`:
124 # * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
127 # Additional relationship for `MVirtualTypeDef`:
129 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
130 # is bound in this definition. Exists only if this definition bound the virtual
131 # type to an effective type.
133 # Additional relationship for `MInnerClassDef`:
135 # * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
140 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
141 # `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
142 # class of the represented entity.
144 # Additional label and relationships for `MClassType`:
146 # * If it is a `MGenericType`, also has the `MGenericType` label.
147 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
148 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
150 # Arguments are named following the `parameter_names` attribute of the
151 # `MClass` referred by `CLASS`.
153 # Additional relationship for `MVirtualType`:
155 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
156 # determines the type (usually a `MVirtualTypeProp`).
158 # Additional attribute and relationship for `MParameterType`:
160 # * `rank`: position of the parameter (0 for the first parameter).
161 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
164 # Additional relationship for `MNullableType`:
166 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
168 # Additional attribute and relationship for `MRawType`:
170 # * `text`: JSON array of the parts’ text.
171 # * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
173 # Additional attribute and relationships for `MSignature`:
175 # * `parameter_names`: JSON array representing the list of the parameter names.
176 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
177 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
180 # In order to maintain the correct parameters order, each `MSignature` node
181 # contains an array of parameter names corresponding to the parameter order in
184 # For example, if the source code contains:
187 # fun foo(a: A, b: B, c: C)
190 # The `MSignature` node will contain a property
191 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
192 # with the parameters in the correct order.
196 # * labels: `MParameter`, `model_name` and `MEntity`.
197 # * `is_vararg`: Is the parameter a vararg?
198 # * `rank`: position of the parameter (0 for the first parameter).
199 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
201 # MParameters are also ranked by their position in the corresponding signature.
202 # Rank 0 for the first parameter, 1 for the next one and etc.
206 # * labels: `MTypePart`, `model_name` and `MEntity`.
207 # * `rank`: position in the `MRawType`.
208 # * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
211 import doc
::model_ext
215 # Helper class that can save and load a `Model` into a Neo4j database.
220 # Because we use only one Neo4j instance to store all the models,
221 # we need to mark their appartenance to a particular model and avoid loading all models.
223 # The name is used as a Neo label on each created nodes and used to load nodes from base.
224 var model_name
: String
226 # The toolcontext used to init the `NeoModel` tool.
227 var toolcontext
: ToolContext
229 # The Neo4j `client` used to communicate with the Neo4j instance.
230 var client
: Neo4jClient
232 # Fill `model` using base pointed by `client`.
233 fun load
(model
: Model): Model do
234 var nodes
: Array[NeoNode]
236 toolcontext
.info
("Loading package node...", 1)
237 nodes
= client
.nodes_with_labels
([model_name
, "MPackage"])
238 for node
in nodes
do to_mpackage
(model
, node
)
239 toolcontext
.info
("Loading groups...", 1)
240 nodes
= client
.nodes_with_labels
([model_name
, "MGroup"])
241 for node
in nodes
do to_mgroup
(model
, node
)
242 toolcontext
.info
("Loading modules...", 1)
243 nodes
= client
.nodes_with_labels
([model_name
, "MModule"])
244 for node
in nodes
do to_mmodule
(model
, node
)
245 toolcontext
.info
("Loading classes...", 1)
246 nodes
= client
.nodes_with_labels
([model_name
, "MClass"])
247 for node
in nodes
do to_mclass
(model
, node
)
248 toolcontext
.info
("Loading class definitions...", 1)
249 nodes
= client
.nodes_with_labels
([model_name
, "MClassDef"])
250 for node
in nodes
do to_mclassdef
(model
, node
)
251 toolcontext
.info
("Loading properties...", 1)
252 nodes
= client
.nodes_with_labels
([model_name
, "MProperty"])
253 for node
in nodes
do to_mproperty
(model
, node
)
254 toolcontext
.info
("Loading property definitions...", 1)
255 nodes
= client
.nodes_with_labels
([model_name
, "MPropDef"])
256 for node
in nodes
do to_mpropdef
(model
, node
)
260 # Save `model` in the base pointed by `client`.
261 fun save
(model
: Model) do
262 var nodes
= collect_model_nodes
(model
)
263 toolcontext
.info
("Save {nodes.length} nodes...", 1)
265 var edges
= collect_model_edges
(model
)
266 toolcontext
.info
("Save {edges.length} edges...", 1)
270 # Save `neo_entities` in base using batch mode.
271 private fun push_all
(neo_entities
: Collection[NeoEntity]) do
272 var batch
= new NeoBatch(client
)
273 var len
= neo_entities
.length
276 for nentity
in neo_entities
do
277 batch
.save_entity
(nentity
)
278 if i
== batch_max_size
then
280 sum
+= batch_max_size
281 toolcontext
.info
(" {sum * 100 / len}% done", 1)
282 batch
= new NeoBatch(client
)
291 # How many operation can be executed in one batch?
292 private var batch_max_size
= 1000
294 # Execute `batch` and check for errors.
296 # Abort if `batch.execute` returns errors.
297 private fun do_batch
(batch
: NeoBatch) do
298 var errors
= batch
.execute
299 if not errors
.is_empty
then
305 # Collect all nodes from the current `model`.
306 private fun collect_model_nodes
(model
: Model): Collection[NeoNode] do
307 for mpackage
in model
.mpackages
do
308 mpackage
.to_node
(nodes
, model_name
)
309 for mgroup
in mpackage
.mgroups
do mgroup
.to_node
(nodes
, model_name
)
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 `MEntity` associated with `node`.
332 fun to_mentity
(model
: Model, node
: NeoNode): MEntity do
333 if node
.labels
.has
("MPackage") then return to_mpackage
(model
, node
)
334 if node
.labels
.has
("MGroup") then return to_mgroup
(model
, node
)
335 if node
.labels
.has
("MModule") then return to_mmodule
(model
, node
)
336 if node
.labels
.has
("MClass") then return to_mclass
(model
, node
)
337 if node
.labels
.has
("MClassDef") then return to_mclassdef
(model
, node
)
338 if node
.labels
.has
("MProperty") then return to_mproperty
(model
, node
)
339 if node
.labels
.has
("MPropDef") then return to_mpropdef
(model
, node
)
340 if node
.labels
.has
("MType") then return to_mtype
(model
, node
)
341 if node
.labels
.has
("MParameter") then return to_mparameter
(model
, node
)
345 # Build a new `MPackage` from a `node`.
347 # REQUIRE `node.labels.has("MPackage")`
348 private fun to_mpackage
(model
: Model, node
: NeoNode): MPackage do
349 var m
= mentities
.get_or_null
(node
.id
.as(Int))
350 if m
isa MPackage then return m
352 assert node
.labels
.has
("MPackage")
353 var location
= to_location
(node
["location"].to_s
)
354 var mpackage
= new MPackage(node
["name"].to_s
, model
, location
)
355 mentities
[node
.id
.as(Int)] = mpackage
356 set_doc
(node
, mpackage
)
357 mpackage
.root
= to_mgroup
(model
, node
.out_nodes
("ROOT").first
)
361 # Build a new `MGroup` from a `node`.
363 # REQUIRE `node.labels.has("MGroup")`
364 private fun to_mgroup
(model
: Model, node
: NeoNode): MGroup do
365 var m
= mentities
.get_or_null
(node
.id
.as(Int))
366 if m
isa MGroup then return m
368 assert node
.labels
.has
("MGroup")
369 var location
= to_location
(node
["location"].to_s
)
370 var mpackage
= to_mpackage
(model
, node
.out_nodes
("PROJECT").first
)
371 var parent
: nullable MGroup = null
372 var out
= node
.out_nodes
("PARENT")
373 if not out
.is_empty
then
374 parent
= to_mgroup
(model
, out
.first
)
376 var mgroup
= new MGroup(node
["name"].to_s
, location
, mpackage
, parent
)
377 mentities
[node
.id
.as(Int)] = mgroup
378 set_doc
(node
, mgroup
)
382 # Build a new `MModule` from a `node`.
384 # REQUIRE `node.labels.has("MModule")`
385 private fun to_mmodule
(model
: Model, node
: NeoNode): MModule do
386 var m
= mentities
.get_or_null
(node
.id
.as(Int))
387 if m
isa MModule then return m
389 assert node
.labels
.has
("MModule")
390 var ins
= node
.in_nodes
("DECLARES")
391 var mgroup
: nullable MGroup = null
392 if not ins
.is_empty
then
393 mgroup
= to_mgroup
(model
, ins
.first
)
395 var name
= node
["name"].to_s
396 var location
= to_location
(node
["location"].to_s
)
397 var mmodule
= new MModule(model
, mgroup
, name
, location
)
398 mentities
[node
.id
.as(Int)] = mmodule
399 set_doc
(node
, mmodule
)
400 var imported_mmodules
= new Array[MModule]
401 for smod
in node
.out_nodes
("IMPORTS") do
402 imported_mmodules
.add to_mmodule
(model
, smod
)
404 mmodule
.set_imported_mmodules
(imported_mmodules
)
408 # Build a new `MClass` from a `node`.
410 # REQUIRE `node.labels.has("MClass")`
411 private fun to_mclass
(model
: Model, node
: NeoNode): MClass do
412 var m
= mentities
.get_or_null
(node
.id
.as(Int))
413 if m
isa MClass then return m
415 assert node
.labels
.has
("MClass")
416 var mmodule
= to_mmodule
(model
, node
.in_nodes
("INTRODUCES").first
)
417 var name
= node
["name"].to_s
418 var location
= to_location
(node
["location"].to_s
)
419 var kind
= to_kind
(node
["kind"].to_s
)
420 var visibility
= to_visibility
(node
["visibility"].to_s
)
421 var parameter_names
= new Array[String]
422 if node
.has_key
("parameter_names") then
423 for e
in node
["parameter_names"].as(JsonArray) do
424 parameter_names
.add e
.to_s
427 var mclass
= new MClass(mmodule
, name
, location
, parameter_names
, kind
, visibility
)
428 mentities
[node
.id
.as(Int)] = mclass
429 set_doc
(node
, mclass
)
433 # Build a new `MClassDef` from a `node`.
435 # REQUIRE `node.labels.has("MClassDef")`
436 private fun to_mclassdef
(model
: Model, node
: NeoNode): MClassDef do
437 var m
= mentities
.get_or_null
(node
.id
.as(Int))
438 if m
isa MClassDef then return m
440 assert node
.labels
.has
("MClassDef")
441 var mmodule
= to_mmodule
(model
, node
.in_nodes
("DEFINES").first
)
442 var mtype
= to_mtype
(model
, node
.out_nodes
("BOUNDTYPE").first
).as(MClassType)
443 var location
= to_location
(node
["location"].to_s
)
444 var mclassdef
= new MClassDef(mmodule
, mtype
, location
)
445 mentities
[node
.id
.as(Int)] = mclassdef
446 set_doc
(node
, mclassdef
)
447 var supertypes
= new Array[MClassType]
448 for sup
in node
.out_nodes
("INHERITS") do
449 supertypes
.add to_mtype
(model
, sup
).as(MClassType)
451 mclassdef
.set_supertypes
(supertypes
)
452 mclassdef
.add_in_hierarchy
456 # Build a new `MProperty` from a `node`.
458 # REQUIRE `node.labels.has("MProperty")`
459 private fun to_mproperty
(model
: Model, node
: NeoNode): MProperty do
460 var m
= mentities
.get_or_null
(node
.id
.as(Int))
461 if m
isa MProperty then return m
463 assert node
.labels
.has
("MProperty")
464 var intro_mclassdef
= to_mclassdef
(model
, node
.out_nodes
("INTRO_CLASSDEF").first
)
465 var name
= node
["name"].to_s
466 var location
= to_location
(node
["location"].to_s
)
467 var visibility
= to_visibility
(node
["visibility"].to_s
)
468 var mprop
: nullable MProperty = null
469 if node
.labels
.has
("MMethod") then
470 mprop
= new MMethod(intro_mclassdef
, name
, location
, visibility
)
471 mprop
.is_init
= node
["is_init"].as(Bool)
472 else if node
.labels
.has
("MAttribute") then
473 mprop
= new MAttribute(intro_mclassdef
, name
, location
, visibility
)
474 else if node
.labels
.has
("MVirtualTypeProp") then
475 mprop
= new MVirtualTypeProp(intro_mclassdef
, name
, location
, visibility
)
476 else if node
.labels
.has
("MInnerClass") then
477 var inner
= to_mclass
(model
, node
.out_nodes
("NESTS").first
)
478 mprop
= new MInnerClass(intro_mclassdef
, name
, location
, visibility
, inner
)
480 if mprop
== null then
481 print
"not yet implemented to_mproperty for {node.labels.join(",")}"
484 mentities
[node
.id
.as(Int)] = mprop
489 # Build a new `MPropDef` from a `node`.
491 # REQUIRE `node.labels.has("MPropDef")`
492 private fun to_mpropdef
(model
: Model, node
: NeoNode): MPropDef do
493 var m
= mentities
.get_or_null
(node
.id
.as(Int))
494 if m
isa MPropDef then return m
496 assert node
.labels
.has
("MPropDef")
497 var mclassdef
= to_mclassdef
(model
, node
.in_nodes
("DECLARES").first
)
498 var mproperty
= to_mproperty
(model
, node
.out_nodes
("DEFINES").first
)
499 var location
= to_location
(node
["location"].to_s
)
500 var mpropdef
: nullable MPropDef = null
501 if node
.labels
.has
("MMethodDef") then
502 mpropdef
= new MMethodDef(mclassdef
, mproperty
.as(MMethod), location
)
503 mpropdef
.is_abstract
= node
["is_abstract"].as(Bool)
504 mpropdef
.is_intern
= node
["is_intern"].as(Bool)
505 mpropdef
.is_extern
= node
["is_extern"].as(Bool)
506 mentities
[node
.id
.as(Int)] = mpropdef
507 mpropdef
.msignature
= to_mtype
(model
, node
.out_nodes
("SIGNATURE").first
).as(MSignature)
508 else if node
.labels
.has
("MAttributeDef") then
509 mpropdef
= new MAttributeDef(mclassdef
, mproperty
.as(MAttribute), location
)
510 mentities
[node
.id
.as(Int)] = mpropdef
511 var static_mtype
= node
.out_nodes
("TYPE")
512 if not static_mtype
.is_empty
then mpropdef
.static_mtype
= to_mtype
(model
, static_mtype
.first
)
513 else if node
.labels
.has
("MVirtualTypeDef") then
514 mpropdef
= new MVirtualTypeDef(mclassdef
, mproperty
.as(MVirtualTypeProp), location
)
515 mentities
[node
.id
.as(Int)] = mpropdef
516 var bound
= node
.out_nodes
("BOUND")
517 if not bound
.is_empty
then mpropdef
.bound
= to_mtype
(model
, bound
.first
)
518 else if node
.labels
.has
("MInnerClassDef") then
519 var inner
= to_mclassdef
(model
, node
.out_nodes
("NESTS").first
)
520 mpropdef
= new MInnerClassDef(mclassdef
,
521 mproperty
.as(MInnerClass), location
, inner
)
522 mentities
[node
.id
.as(Int)] = mpropdef
524 if mpropdef
== null then
525 print
"not yet implemented to_mpropdef for {node.labels.join(",")}"
528 set_doc
(node
, mpropdef
)
532 # Build a new `MType` from a `node`.
534 # REQUIRE `node.labels.has("MType")`
535 private fun to_mtype
(model
: Model, node
: NeoNode): MType do
536 var m
= mentities
.get_or_null
(node
.id
.as(Int))
537 if m
isa MType then return m
539 assert node
.labels
.has
("MType")
540 if node
.labels
.has
("MClassType") then
541 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
542 var args
= new Array[MType]
543 for narg
in node
.out_nodes
("ARGUMENT") do
544 args
.add to_mtype
(model
, narg
)
546 var mtype
= mclass
.get_mtype
(args
)
547 mentities
[node
.id
.as(Int)] = mtype
549 else if node
.labels
.has
("MParameterType") then
550 var mclass
= to_mclass
(model
, node
.out_nodes
("CLASS").first
)
551 var rank
= node
["rank"].to_s
.to_i
552 var mtype
= mclass
.mparameters
[rank
]
553 mentities
[node
.id
.as(Int)] = mtype
555 else if node
.labels
.has
("MNullableType") then
556 var intype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
557 var mtype
= intype
.as_nullable
558 mentities
[node
.id
.as(Int)] = mtype
560 else if node
.labels
.has
("MVirtualType") then
561 var mproperty
= to_mproperty
(model
, node
.out_nodes
("PROPERTY").first
)
562 assert mproperty
isa MVirtualTypeProp
563 var mtype
= mproperty
.mvirtualtype
564 mentities
[node
.id
.as(Int)] = mtype
566 else if node
.labels
.has
("MSignature") then
567 # Get all param nodes
568 var mparam_nodes
= new HashMap[String, MParameter]
569 for pnode
in node
.out_nodes
("PARAMETER") do
570 var mparam
= to_mparameter
(model
, pnode
)
571 mparam_nodes
[mparam
.name
] = mparam
573 # Load params in the good order
574 var mparam_names
= node
["parameter_names"]
575 var mparameters
= new Array[MParameter]
576 if mparam_names
isa JsonArray then
577 for mparam_name
in mparam_names
do
578 var mparam
= mparam_nodes
[mparam_name
.to_s
]
579 mparameters
.add mparam
582 var return_mtype
: nullable MType = null
583 var ret_nodes
= node
.out_nodes
("RETURNTYPE")
584 if not ret_nodes
.is_empty
then
585 return_mtype
= to_mtype
(model
, ret_nodes
.first
)
587 var mtype
= new MSignature(mparameters
, return_mtype
)
588 mentities
[node
.id
.as(Int)] = mtype
590 else if node
.labels
.has
("MRawType") then
591 var mtype
= new MRawType(model
)
592 var parts
= node
["text"]
593 if parts
isa JsonArray then
595 mtype
.parts
.add
(new MTypePart(model
, p
.to_s
, null))
597 for pnode
in node
.out_nodes
("LINK") do
598 assert pnode
.labels
.has
("MTypePart")
599 if not pnode
.out_nodes
("TARGET").is_empty
then
600 var rank
= pnode
["rank"]
601 var target
= to_mentity
(model
, pnode
.out_nodes
("TARGET").first
)
603 mtype
.parts
[rank
] = mtype
.parts
[rank
].link_to
(target
)
607 mentities
[node
.id
.as(Int)] = mtype
610 print
"not yet implemented to_mtype for {node.labels.join(",")}"
614 # Build a new `MParameter` from `node`.
616 # REQUIRE `node.labels.has("MParameter")`
617 private fun to_mparameter
(model
: Model, node
: NeoNode): MParameter do
618 var m
= mentities
.get_or_null
(node
.id
.as(Int))
619 if m
isa MParameter then return m
621 assert node
.labels
.has
("MParameter")
622 var name
= node
["name"].to_s
623 var mtype
= to_mtype
(model
, node
.out_nodes
("TYPE").first
)
624 var is_vararg
= node
["is_vararg"].as(Bool)
625 var mparameter
= new MParameter(name
, mtype
, is_vararg
)
626 mentities
[node
.id
.as(Int)] = mparameter
630 # Get a `Location` from its string representation.
631 private fun to_location
(loc
: String): nitc
::Location do
632 return new nitc
::Location.from_string
(loc
)
635 # Get a `MVisibility` from its string representation.
636 private fun to_visibility
(vis
: String): MVisibility do
637 if vis
== intrude_visibility
.to_s
then
638 return intrude_visibility
639 else if vis
== public_visibility
.to_s
then
640 return public_visibility
641 else if vis
== protected_visibility
.to_s
then
642 return protected_visibility
643 else if vis
== private_visibility
.to_s
then
644 return private_visibility
645 else if vis
== package_visibility
.to_s
then
646 return package_visibility
648 return none_visibility
652 # Get a `MKind` from its string representation.
653 private fun to_kind
(kind
: String): MClassKind do
654 if kind
== abstract_kind
.to_s
then
656 else if kind
== concrete_kind
.to_s
then
658 else if kind
== interface_kind
.to_s
then
659 return interface_kind
660 else if kind
== enum_kind
.to_s
then
662 else if kind
== extern_kind
.to_s
then
665 return raw_kind
(kind
)
669 # Extract the `MDoc` from `node` and link it to `mentity`.
670 private fun set_doc
(node
: NeoNode, mentity
: MEntity) do
671 if node
.has_key
("mdoc") then
672 var lines
= new Array[String]
673 for e
in node
["mdoc"].as(JsonArray) do
674 lines
.add e
.to_s
#.replace("\n", "\\n")
676 var location
= to_location
(node
["mdoc_location"].to_s
)
677 var mdoc
= new MDoc(location
)
678 mdoc
.content
.add_all
(lines
)
679 mdoc
.original_mentity
= mentity
686 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
687 if nodes
.has_key
(self) then return nodes
[self]
691 node
.out_edges
.add
(new NeoEdge(node
, "ROOT", root
.to_node
(nodes
, model_name
)))
698 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
699 if nodes
.has_key
(self) then return nodes
[self]
702 node
.out_edges
.add
(new NeoEdge(node
, "PROJECT", mpackage
.to_node
(nodes
, model_name
)))
703 if parent
!= null then
704 node
.out_edges
.add
(new NeoEdge(node
, "PARENT", parent
.to_node
(nodes
, model_name
)))
706 for mmodule
in mmodules
do
707 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", mmodule
.to_node
(nodes
, model_name
)))
709 for subgroup
in in_nesting
.direct_smallers
do
710 node
.in_edges
.add
(new NeoEdge(node
, "NESTS", subgroup
.to_node
(nodes
, model_name
)))
717 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
718 if nodes
.has_key
(self) then return nodes
[self]
720 for parent
in in_importation
.direct_greaters
do
721 node
.out_edges
.add
(new NeoEdge(node
, "IMPORTS", parent
.to_node
(nodes
, model_name
)))
723 for mclass
in intro_mclasses
do
724 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", mclass
.to_node
(nodes
, model_name
)))
726 for mclassdef
in mclassdefs
do
727 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", mclassdef
.to_node
(nodes
, model_name
)))
734 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
735 if nodes
.has_key
(self) then return nodes
[self]
737 node
["kind"] = kind
.to_s
738 node
["visibility"] = visibility
.to_s
739 if not mparameters
.is_empty
then
740 var parameter_names
= new Array[String]
741 for p
in mparameters
do parameter_names
.add
(p
.name
)
742 node
["parameter_names"] = new JsonArray.from
(parameter_names
)
744 node
.out_edges
.add
(new NeoEdge(node
, "CLASSTYPE", mclass_type
.to_node
(nodes
, model_name
)))
749 redef class MClassDef
750 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
751 if nodes
.has_key
(self) then return nodes
[self]
753 node
.out_edges
.add
(new NeoEdge(node
, "BOUNDTYPE", bound_mtype
.to_node
(nodes
, model_name
)))
754 node
.out_edges
.add
(new NeoEdge(node
, "MCLASS", mclass
.to_node
(nodes
, model_name
)))
755 for mproperty
in intro_mproperties
do
756 node
.out_edges
.add
(new NeoEdge(node
, "INTRODUCES", mproperty
.to_node
(nodes
, model_name
)))
758 for mpropdef
in mpropdefs
do
759 node
.out_edges
.add
(new NeoEdge(node
, "DECLARES", mpropdef
.to_node
(nodes
, model_name
)))
761 for sup
in supertypes
do
762 node
.out_edges
.add
(new NeoEdge(node
, "INHERITS", sup
.to_node
(nodes
, model_name
)))
768 redef class MProperty
769 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
770 if nodes
.has_key
(self) then return nodes
[self]
771 var node
= make_node
(nodes
, model_name
)
772 node
.labels
.add
"MProperty"
773 node
["visibility"] = visibility
.to_s
774 node
.out_edges
.add
(new NeoEdge(node
, "INTRO_CLASSDEF", intro_mclassdef
.to_node
(nodes
, model_name
)))
775 node
.labels
.add
self.class_name
781 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
782 if nodes
.has_key
(self) then return nodes
[self]
784 node
["is_init"] = is_init
789 redef class MInnerClass
790 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
791 if nodes
.has_key
(self) then return nodes
[self]
793 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", inner
.to_node
(nodes
, model_name
)))
799 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
800 if nodes
.has_key
(self) then return nodes
[self]
801 var node
= make_node
(nodes
, model_name
)
802 node
.labels
.add
"MPropDef"
803 node
.out_edges
.add
(new NeoEdge(node
, "DEFINES", mproperty
.to_node
(nodes
, model_name
)))
804 node
.labels
.add
self.class_name
809 redef class MMethodDef
810 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
811 if nodes
.has_key
(self) then return nodes
[self]
813 node
["is_abstract"] = is_abstract
814 node
["is_intern"] = is_intern
815 node
["is_extern"] = is_extern
816 var msignature
= msignature
817 if msignature
!= null then
818 node
.out_edges
.add
(new NeoEdge(node
, "SIGNATURE", msignature
.to_node
(nodes
, model_name
)))
824 redef class MAttributeDef
825 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
826 if nodes
.has_key
(self) then return nodes
[self]
828 var static_mtype
= static_mtype
829 if static_mtype
!= null then
830 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", static_mtype
.to_node
(nodes
, model_name
)))
836 redef class MVirtualTypeDef
837 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
838 if nodes
.has_key
(self) then return nodes
[self]
841 if bound
!= null then
842 node
.out_edges
.add
(new NeoEdge(node
, "BOUND", bound
.to_node
(nodes
, model_name
)))
848 redef class MInnerClassDef
849 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
850 if nodes
.has_key
(self) then return nodes
[self]
852 node
.out_edges
.add
(new NeoEdge(node
, "NESTS", inner
.to_node
(nodes
, model_name
)))
857 redef class MTypePart
858 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
859 if nodes
.has_key
(self) then return nodes
[self]
861 if self.target
!= null then
862 var target_node
= self.target
.as(not null).to_node
(nodes
, model_name
)
863 node
.out_edges
.add
(new NeoEdge(node
, "TARGET", target_node
))
869 redef class MParameter
870 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
871 if nodes
.has_key
(self) then return nodes
[self]
873 node
["name"] = self.name
874 node
["is_vararg"] = self.is_vararg
875 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", self.mtype
.to_node
(nodes
, model_name
)))
880 redef class MClassType
881 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
882 if nodes
.has_key
(self) then return nodes
[self]
884 node
.labels
.add
"MClassType"
885 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", mclass
.to_node
(nodes
, model_name
)))
886 for arg
in arguments
do
887 node
.out_edges
.add
(new NeoEdge(node
, "ARGUMENT", arg
.to_node
(nodes
, model_name
)))
894 redef class MGenericType
895 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
896 if nodes
.has_key
(self) then return nodes
[self]
898 node
.labels
.add
"MGenericType"
903 redef class MVirtualType
904 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
905 if nodes
.has_key
(self) then return nodes
[self]
907 node
.labels
.add
"MVirtualType"
908 node
.out_edges
.add
(new NeoEdge(node
, "PROPERTY", mproperty
.to_node
(nodes
, model_name
)))
913 redef class MParameterType
914 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
915 if nodes
.has_key
(self) then return nodes
[self]
917 node
.labels
.add
"MParameterType"
919 node
.out_edges
.add
(new NeoEdge(node
, "CLASS", mclass
.to_node
(nodes
, model_name
)))
924 redef class MNullableType
925 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
926 if nodes
.has_key
(self) then return nodes
[self]
928 node
.labels
.add
"MNullableType"
929 node
.out_edges
.add
(new NeoEdge(node
, "TYPE", mtype
.to_node
(nodes
, model_name
)))
934 redef class MSignature
935 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
936 if nodes
.has_key
(self) then return nodes
[self]
938 node
.labels
.add
"MSignature"
939 var names
= new JsonArray
941 for mparameter
in mparameters
do
942 names
.add mparameter
.name
943 var pnode
= mparameter
.to_node
(nodes
, model_name
)
945 node
.out_edges
.add
(new NeoEdge(node
, "PARAMETER", pnode
))
948 if not names
.is_empty
then node
["parameter_names"] = names
949 var return_mtype
= return_mtype
950 if return_mtype
!= null then
951 node
.out_edges
.add
(new NeoEdge(node
, "RETURNTYPE", return_mtype
.to_node
(nodes
, model_name
)))
958 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
959 if nodes
.has_key
(self) then return nodes
[self]
961 node
.labels
.add
"MRawType"
962 var text
= new JsonArray
964 for part
in self.parts
do
966 if part
.target
!= null then
967 var pnode
= part
.to_node
(nodes
, model_name
)
969 node
.out_edges
.add
(new NeoEdge(node
, "LINK", pnode
))
973 if not text
.is_empty
then node
["text"] = text
979 redef fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
980 if nodes
.has_key
(self) then return nodes
[self]
981 var node
= make_node
(nodes
, model_name
)
982 node
.labels
.add
"MType"
988 # Build a `NeoNode` representing `self`.
989 private fun to_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
990 if nodes
.has_key
(self) then return nodes
[self]
991 var node
= make_node
(nodes
, model_name
)
992 node
.labels
.add
self.class_name
996 # Make a new `NeoNode` based on `mentity`.
997 private fun make_node
(nodes
: HashMap[MEntity, NeoNode], model_name
: nullable String): NeoNode do
998 var node
= new NeoNode
1000 node
.labels
.add
"MEntity"
1001 if model_name
!= null then node
.labels
.add model_name
1002 node
["name"] = self.name
1003 if not self isa MSignature then
1004 #FIXME: MSignature is a MEntity, but has no model :/
1005 node
["location"] = self.location
.to_s
1007 var mdoc
= self.mdoc
1008 if mdoc
!= null then
1009 node
["mdoc"] = new JsonArray.from
(mdoc
.content
)
1010 node
["mdoc_location"] = mdoc
.location
.to_s