#
# Note : All nodes described here are MEntities.
#
-# `MProject`
+# `MPackage`
#
-# * labels: `MProject`, `model_name` and `MEntity`.
-# * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree.
+# * labels: `MPackage`, `model_name` and `MEntity`.
+# * `(:MPackage)-[:ROOT]->(:MGroup)`: root of the group tree.
#
# `MGroup`
#
# * labels: `MGroup`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
-# * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
+# * `(:MGroup)-[:PROJECT]->(:MPackage)`: associated package.
# * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
# group.
# * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
# `MModule`
#
# * labels: `MModule`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
# * `location`: origin of the definition. SEE: `Location.to_s`
# * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
# * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
# `MClass`
#
# * labels: `MClass`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
# * `kind`: kind of the class (`interface`, `abstract class`, etc.)
# * `visibility`: visibility of the class.
# * `parameter_names`: JSON array listing the name of each formal generic
# `MProperty`
#
# * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
-# `MAttribute` or `MVirtualTypeProp`, depending on the class of the represented
-# entity.
-# * `full_name`: fully qualified name.
+# `MAttribute` `MVirtualTypeProp` or `MInnerClass`, depending on the class of
+# the represented entity.
# * `visibility`: visibility of the property.
# * `is_init`: Indicates if the property is a constructor. Exists only if the
# node is a `MMethod`.
# * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
# the property.
#
+# Additional relationship for `MInnerClass`:
+#
+# * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
+#
# `MPropDef`
#
# * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
-# `MAttributeDef` or `MVirtualTypeDef`, depending on the class of the
-# represented entity.
+# `MAttributeDef`, `MVirtualTypeDef` or `MInnerClassDef`, depending on the
+# class of the represented entity.
# * `location`: origin of the definition. SEE: `Location.to_s`.
# * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
#
# is bound in this definition. Exists only if this definition bound the virtual
# type to an effective type.
#
+# Additional relationship for `MInnerClassDef`:
+#
+# * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
+# definition.
+#
# `MType`
#
# * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
#
# For example, if the source code contains:
#
+# ~~~nitish
# fun foo(a: A, b: B, c: C)
+# ~~~
#
# The `MSignature` node will contain a property
# `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
# Fill `model` using base pointed by `client`.
fun load(model: Model): Model do
- toolcontext.info("Locate all mentities...", 1)
- var nodes = client.nodes_with_label(model_name)
-
- toolcontext.info("Preload nodes...", 1)
- pull_all_nodes(nodes)
- toolcontext.info("Preload edges...", 1)
- pull_all_edges(nodes)
+ var nodes: Array[NeoNode]
- toolcontext.info("Build model...", 1)
- nodes = client.nodes_with_labels([model_name, "MProject"])
- for node in nodes do to_mproject(model, node)
+ toolcontext.info("Loading package node...", 1)
+ nodes = client.nodes_with_labels([model_name, "MPackage"])
+ for node in nodes do to_mpackage(model, node)
+ toolcontext.info("Loading groups...", 1)
nodes = client.nodes_with_labels([model_name, "MGroup"])
for node in nodes do to_mgroup(model, node)
+ toolcontext.info("Loading modules...", 1)
nodes = client.nodes_with_labels([model_name, "MModule"])
for node in nodes do to_mmodule(model, node)
+ toolcontext.info("Loading classes...", 1)
nodes = client.nodes_with_labels([model_name, "MClass"])
for node in nodes do to_mclass(model, node)
+ toolcontext.info("Loading class definitions...", 1)
nodes = client.nodes_with_labels([model_name, "MClassDef"])
for node in nodes do to_mclassdef(model, node)
+ toolcontext.info("Loading properties...", 1)
nodes = client.nodes_with_labels([model_name, "MProperty"])
for node in nodes do to_mproperty(model, node)
+ toolcontext.info("Loading property definitions...", 1)
nodes = client.nodes_with_labels([model_name, "MPropDef"])
for node in nodes do to_mpropdef(model, node)
return model
do_batch(batch)
end
- # Load content for all `nodes` from base.
- #
- # Content corresponds to properties and labels that are loaded in batch mode.
- private fun pull_all_nodes(nodes: Collection[NeoNode]) do
- var batch = new NeoBatch(client)
- var len = nodes.length
- var sum = 0
- var i = 1
- for node in nodes do
- batch.load_node(node)
- if i == batch_max_size then
- do_batch(batch)
- sum += batch_max_size
- toolcontext.info(" {sum * 100 / len}% done", 1)
- batch = new NeoBatch(client)
- i = 1
- else
- i += 1
- end
- end
- do_batch(batch)
- end
-
- # Load all edges from base linked to `nodes`.
- #
- # Edges are loaded in batch mode.
- private fun pull_all_edges(nodes: Collection[NeoNode]) do
- var batch = new NeoBatch(client)
- var len = nodes.length
- var sum = 0
- var i = 1
- for node in nodes do
- batch.load_node_edges(node)
- if i == batch_max_size then
- do_batch(batch)
- sum += batch_max_size
- toolcontext.info(" {sum * 100 / len}% done", 1)
- batch = new NeoBatch(client)
- i = 1
- else
- i += 1
- end
- end
- do_batch(batch)
- end
-
# How many operation can be executed in one batch?
private var batch_max_size = 1000
# Collect all nodes from the current `model`.
private fun collect_model_nodes(model: Model): Collection[NeoNode] do
- for mproject in model.mprojects do
- to_node(mproject)
- for mgroup in mproject.mgroups do to_node(mgroup)
+ for mpackage in model.mpackages do
+ to_node(mpackage)
+ for mgroup in mpackage.mgroups do to_node(mgroup)
end
return nodes.values
end
end
# Mentities associated to nodes.
- private var mentities = new HashMap[NeoNode, MEntity]
+ #
+ # The key is the node’s id.
+ private var mentities = new HashMap[Int, MEntity]
# Nodes associated with MEntities.
private var nodes = new HashMap[MEntity, NeoNode]
# `mentities` are stored locally to avoid duplication.
fun to_node(mentity: MEntity): NeoNode do
if nodes.has_key(mentity) then return nodes[mentity]
- if mentity isa MProject then return mproject_node(mentity)
+ if mentity isa MPackage then return mpackage_node(mentity)
if mentity isa MGroup then return mgroup_node(mentity)
if mentity isa MModule then return mmodule_node(mentity)
if mentity isa MClass then return mclass_node(mentity)
# Get the `MEntity` associated with `node`.
fun to_mentity(model: Model, node: NeoNode): MEntity do
- if node.labels.has("MProject") then return to_mproject(model, node)
+ if node.labels.has("MPackage") then return to_mpackage(model, node)
if node.labels.has("MGroup") then return to_mgroup(model, node)
if node.labels.has("MModule") then return to_mmodule(model, node)
if node.labels.has("MClass") then return to_mclass(model, node)
node.labels.add "MEntity"
node.labels.add model_name
node["name"] = mentity.name
- if mentity.mdoc != null then node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
+ if mentity.mdoc != null then
+ node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
+ node["mdoc_location"] = mentity.mdoc.location.to_s
+ end
return node
end
- # Build a `NeoNode` representing `mproject`.
- private fun mproject_node(mproject: MProject): NeoNode do
- var node = make_node(mproject)
- node.labels.add "MProject"
- var root = mproject.root
+ # Build a `NeoNode` representing `mpackage`.
+ private fun mpackage_node(mpackage: MPackage): NeoNode do
+ var node = make_node(mpackage)
+ node.labels.add "MPackage"
+ var root = mpackage.root
if root != null then
node.out_edges.add(new NeoEdge(node, "ROOT", to_node(root)))
end
return node
end
- # Build a new `MProject` from a `node`.
+ # Build a new `MPackage` from a `node`.
#
- # REQUIRE `node.labels.has("MProject")`
- private fun to_mproject(model: Model, node: NeoNode): MProject do
- if mentities.has_key(node) then return mentities[node].as(MProject)
- assert node.labels.has("MProject")
- var mproject = new MProject(node["name"].to_s, model)
- mentities[node] = mproject
- set_doc(node, mproject)
- mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
- return mproject
+ # REQUIRE `node.labels.has("MPackage")`
+ private fun to_mpackage(model: Model, node: NeoNode): MPackage do
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MPackage then return m
+
+ assert node.labels.has("MPackage")
+ var mpackage = new MPackage(node["name"].to_s, model)
+ mentities[node.id.as(Int)] = mpackage
+ set_doc(node, mpackage)
+ mpackage.root = to_mgroup(model, node.out_nodes("ROOT").first)
+ return mpackage
end
# Build a `NeoNode` representing `mgroup`.
private fun mgroup_node(mgroup: MGroup): NeoNode do
var node = make_node(mgroup)
node.labels.add "MGroup"
- node["full_name"] = mgroup.full_name
var parent = mgroup.parent
- node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mproject)))
+ node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mpackage)))
if parent != null then
node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
end
#
# REQUIRE `node.labels.has("MGroup")`
private fun to_mgroup(model: Model, node: NeoNode): MGroup do
- if mentities.has_key(node) then return mentities[node].as(MGroup)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MGroup then return m
+
assert node.labels.has("MGroup")
- var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
+ var mpackage = to_mpackage(model, node.out_nodes("PROJECT").first)
var parent: nullable MGroup = null
var out = node.out_nodes("PARENT")
if not out.is_empty then
parent = to_mgroup(model, out.first)
end
- var mgroup = new MGroup(node["name"].to_s, mproject, parent)
- mentities[node] = mgroup
+ var mgroup = new MGroup(node["name"].to_s, mpackage, parent)
+ mentities[node.id.as(Int)] = mgroup
set_doc(node, mgroup)
return mgroup
end
private fun mmodule_node(mmodule: MModule): NeoNode do
var node = make_node(mmodule)
node.labels.add "MModule"
- node["full_name"] = mmodule.full_name
node["location"] = mmodule.location.to_s
for parent in mmodule.in_importation.direct_greaters do
node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
#
# REQUIRE `node.labels.has("MModule")`
private fun to_mmodule(model: Model, node: NeoNode): MModule do
- if mentities.has_key(node) then return mentities[node].as(MModule)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MModule then return m
+
assert node.labels.has("MModule")
var ins = node.in_nodes("DECLARES")
var mgroup: nullable MGroup = null
var name = node["name"].to_s
var location = to_location(node["location"].to_s)
var mmodule = new MModule(model, mgroup, name, location)
- mentities[node] = mmodule
+ mentities[node.id.as(Int)] = mmodule
set_doc(node, mmodule)
var imported_mmodules = new Array[MModule]
for smod in node.out_nodes("IMPORTS") do
private fun mclass_node(mclass: MClass): NeoNode do
var node = make_node(mclass)
node.labels.add "MClass"
- node["full_name"] = mclass.full_name
node["kind"] = mclass.kind.to_s
node["visibility"] = mclass.visibility.to_s
if not mclass.mparameters.is_empty then
#
# REQUIRE `node.labels.has("MClass")`
private fun to_mclass(model: Model, node: NeoNode): MClass do
- if mentities.has_key(node) then return mentities[node].as(MClass)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MClass then return m
+
assert node.labels.has("MClass")
var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
var name = node["name"].to_s
end
end
var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
- mentities[node] = mclass
+ mentities[node.id.as(Int)] = mclass
set_doc(node, mclass)
return mclass
end
#
# REQUIRE `node.labels.has("MClassDef")`
private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
- if mentities.has_key(node) then return mentities[node].as(MClassDef)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MClassDef then return m
+
assert node.labels.has("MClassDef")
var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
var location = to_location(node["location"].to_s)
var mclassdef = new MClassDef(mmodule, mtype, location)
- mentities[node] = mclassdef
+ mentities[node.id.as(Int)] = mclassdef
set_doc(node, mclassdef)
var supertypes = new Array[MClassType]
for sup in node.out_nodes("INHERITS") do
private fun mproperty_node(mproperty: MProperty): NeoNode do
var node = make_node(mproperty)
node.labels.add "MProperty"
- node["full_name"] = mproperty.full_name
node["visibility"] = mproperty.visibility.to_s
if mproperty isa MMethod then
node.labels.add "MMethod"
node.labels.add "MAttribute"
else if mproperty isa MVirtualTypeProp then
node.labels.add "MVirtualTypeProp"
+ else if mproperty isa MInnerClass then
+ node.labels.add "MInnerClass"
+ node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mproperty.inner)))
end
node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
return node
#
# REQUIRE `node.labels.has("MProperty")`
private fun to_mproperty(model: Model, node: NeoNode): MProperty do
- if mentities.has_key(node) then return mentities[node].as(MProperty)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MProperty then return m
+
assert node.labels.has("MProperty")
var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
var name = node["name"].to_s
mprop = new MAttribute(intro_mclassdef, name, visibility)
else if node.labels.has("MVirtualTypeProp") then
mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
+ else if node.labels.has("MInnerClass") then
+ var inner = to_mclass(model, node.out_nodes("NESTS").first)
+ mprop = new MInnerClass(intro_mclassdef, name, visibility, inner)
end
if mprop == null then
print "not yet implemented to_mproperty for {node.labels.join(",")}"
abort
end
- mentities[node] = mprop
+ mentities[node.id.as(Int)] = mprop
set_doc(node, mprop)
return mprop
end
if bound != null then
node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
end
+ else if mpropdef isa MInnerClassDef then
+ node.labels.add "MInnerClassDef"
+ node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mpropdef.inner)))
end
return node
end
#
# REQUIRE `node.labels.has("MPropDef")`
private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
- if mentities.has_key(node) then return mentities[node].as(MPropDef)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MPropDef then return m
+
assert node.labels.has("MPropDef")
var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
mpropdef.is_abstract = node["is_abstract"].as(Bool)
mpropdef.is_intern = node["is_intern"].as(Bool)
mpropdef.is_extern = node["is_extern"].as(Bool)
- mentities[node] = mpropdef
+ mentities[node.id.as(Int)] = mpropdef
mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
else if node.labels.has("MAttributeDef") then
mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
- mentities[node] = mpropdef
+ mentities[node.id.as(Int)] = mpropdef
var static_mtype = node.out_nodes("TYPE")
if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
else if node.labels.has("MVirtualTypeDef") then
mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
- mentities[node] = mpropdef
+ mentities[node.id.as(Int)] = mpropdef
var bound = node.out_nodes("BOUND")
if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
+ else if node.labels.has("MInnerClassDef") then
+ var inner = to_mclassdef(model, node.out_nodes("NESTS").first)
+ mpropdef = new MInnerClassDef(mclassdef,
+ mproperty.as(MInnerClass), location, inner)
+ mentities[node.id.as(Int)] = mpropdef
end
if mpropdef == null then
print "not yet implemented to_mpropdef for {node.labels.join(",")}"
#
# REQUIRE `node.labels.has("MType")`
private fun to_mtype(model: Model, node: NeoNode): MType do
- if mentities.has_key(node) then return mentities[node].as(MType)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MType then return m
+
assert node.labels.has("MType")
if node.labels.has("MClassType") then
var mclass = to_mclass(model, node.out_nodes("CLASS").first)
args.add to_mtype(model, narg)
end
var mtype = mclass.get_mtype(args)
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
else if node.labels.has("MParameterType") then
var mclass = to_mclass(model, node.out_nodes("CLASS").first)
var rank = node["rank"].to_s.to_i
var mtype = mclass.mparameters[rank]
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
else if node.labels.has("MNullableType") then
var intype = to_mtype(model, node.out_nodes("TYPE").first)
var mtype = intype.as_nullable
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
else if node.labels.has("MVirtualType") then
var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
assert mproperty isa MVirtualTypeProp
var mtype = mproperty.mvirtualtype
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
else if node.labels.has("MSignature") then
# Get all param nodes
return_mtype = to_mtype(model, ret_nodes.first)
end
var mtype = new MSignature(mparameters, return_mtype)
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
else if node.labels.has("MRawType") then
var mtype = new MRawType(model)
end
end
end
- mentities[node] = mtype
+ mentities[node.id.as(Int)] = mtype
return mtype
end
print "not yet implemented to_mtype for {node.labels.join(",")}"
#
# REQUIRE `node.labels.has("MParameter")`
private fun to_mparameter(model: Model, node: NeoNode): MParameter do
- if mentities.has_key(node) then return mentities[node].as(MParameter)
+ var m = mentities.get_or_null(node.id.as(Int))
+ if m isa MParameter then return m
+
assert node.labels.has("MParameter")
var name = node["name"].to_s
var mtype = to_mtype(model, node.out_nodes("TYPE").first)
var is_vararg = node["is_vararg"].as(Bool)
var mparameter = new MParameter(name, mtype, is_vararg)
- mentities[node] = mparameter
+ mentities[node.id.as(Int)] = mparameter
return mparameter
end
# Get a `Location` from its string representation.
private fun to_location(loc: String): Location do
- #TODO filepath
- var parts = loc.split_with(":")
- var file = new SourceFile.from_string(parts[0], "")
- var pos = parts[1].split_with("--")
- var pos1 = pos[0].split_with(",")
- var pos2 = pos[1].split_with(",")
- var line_s = pos1[0].to_i
- var line_e = pos2[0].to_i
- var column_s = pos1[1].to_i
- var column_e = 0
- if pos2.length == 2 then pos2[1].to_i
- return new Location(file, line_s, line_e, column_s, column_e)
+ return new Location.from_string(loc)
end
# Get a `MVisibility` from its string representation.
for e in node["mdoc"].as(JsonArray) do
lines.add e.to_s#.replace("\n", "\\n")
end
- var mdoc = new MDoc
+ var location = to_location(node["mdoc_location"].to_s)
+ var mdoc = new MDoc(location)
mdoc.content.add_all(lines)
mdoc.original_mentity = mentity
mentity.mdoc = mdoc