Merge: neo: Move neo node creation
[nit.git] / src / neo.nit
index df2792c..be92ab6 100644 (file)
 #
 # 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
@@ -55,7 +54,6 @@
 # `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
@@ -66,7 +64,6 @@
 # `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`,
-# `MNullableType`, `MVirtualType` or `MSignature`, depending on the class of
-# the represented entity.
+# `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
+# class of the represented entity.
 #
 # Additional label and relationships for `MClassType`:
 #
 #
 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
 #
+# Additional attribute and relationship for `MRawType`:
+#
+# * `text`: JSON array of the parts’ text.
+# * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
+#
 # Additional attribute and relationships for `MSignature`:
 #
 # * `parameter_names`: JSON array representing the list of the parameter names.
 #
 # 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
 #
 # MParameters are also ranked by their position in the corresponding signature.
 # Rank 0 for the first parameter, 1 for the next one and etc.
+#
+# `MTypePart`
+#
+# * labels: `MTypePart`, `model_name` and `MEntity`.
+# * `rank`: position in the `MRawType`.
+# * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
 module neo
 
-import model
+import doc::model_ext
 import neo4j
 import toolcontext
 
@@ -213,27 +231,27 @@ class NeoModel
 
        # 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)
+               var nodes: Array[NeoNode]
 
-               toolcontext.info("Preload nodes...", 1)
-               pull_all_nodes(nodes)
-               toolcontext.info("Preload edges...", 1)
-               pull_all_edges(nodes)
-
-               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
@@ -270,52 +288,6 @@ class NeoModel
                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
 
@@ -332,9 +304,9 @@ class NeoModel
 
        # 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
+                       mpackage.to_node(nodes, model_name)
+                       for mgroup in mpackage.mgroups do mgroup.to_node(nodes, model_name)
                end
                return nodes.values
        end
@@ -349,122 +321,71 @@ class NeoModel
        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]
 
-       # Get the `NeoNode` associated with `mentity`.
-       # `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 MGroup then return mgroup_node(mentity)
-               if mentity isa MModule then return mmodule_node(mentity)
-               if mentity isa MClass then return mclass_node(mentity)
-               if mentity isa MClassDef then return mclassdef_node(mentity)
-               if mentity isa MProperty then return mproperty_node(mentity)
-               if mentity isa MPropDef then return mpropdef_node(mentity)
-               if mentity isa MType then return mtype_node(mentity)
-               if mentity isa MParameter then return mparameter_node(mentity)
+       # Get the `MEntity` associated with `node`.
+       fun to_mentity(model: Model, node: NeoNode): MEntity do
+               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)
+               if node.labels.has("MClassDef") then return to_mclassdef(model, node)
+               if node.labels.has("MProperty") then return to_mproperty(model, node)
+               if node.labels.has("MPropDef") then return to_mpropdef(model, node)
+               if node.labels.has("MType") then return to_mtype(model, node)
+               if node.labels.has("MParameter") then return to_mparameter(model, node)
                abort
        end
 
-       # Make a new `NeoNode` based on `mentity`.
-       private fun make_node(mentity: MEntity): NeoNode do
-               var node = new NeoNode
-               nodes[mentity] = 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)
-               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
-               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
-       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)))
-               if parent != null then
-                       node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
-               end
-               for mmodule in mgroup.mmodules do
-                       node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mmodule)))
-               end
-               for subgroup in mgroup.in_nesting.direct_smallers do
-                       node.in_edges.add(new NeoEdge(node, "NESTS", to_node(subgroup)))
-               end
-               return node
+       # 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 location = to_location(node["location"].to_s)
+               var mpackage = new MPackage(node["name"].to_s, model, location)
+               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 new `MGroup` from a `node`.
        #
        # 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 location = to_location(node["location"].to_s)
+               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, location, mpackage, parent)
+               mentities[node.id.as(Int)] = mgroup
                set_doc(node, mgroup)
                return mgroup
        end
 
-       # Build a `NeoNode` representing `mmodule`.
-       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)))
-               end
-               for mclass in mmodule.intro_mclasses do
-                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mclass)))
-               end
-               for mclassdef in mmodule.mclassdefs do
-                       node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mclassdef)))
-               end
-               return node
-       end
-
        # Build a new `MModule` from a `node`.
        #
        # 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
@@ -474,7 +395,7 @@ class NeoModel
                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
@@ -484,30 +405,17 @@ class NeoModel
                return mmodule
        end
 
-       # Build a `NeoNode` representing `mclass`.
-       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
-                       var parameter_names = new Array[String]
-                       for p in mclass.mparameters do parameter_names.add(p.name)
-                       node["parameter_names"] = new JsonArray.from(parameter_names)
-               end
-               node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type)))
-               return node
-       end
-
        # Build a new `MClass` from a `node`.
        #
        # 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
+               var location = to_location(node["location"].to_s)
                var kind = to_kind(node["kind"].to_s)
                var visibility = to_visibility(node["visibility"].to_s)
                var parameter_names = new Array[String]
@@ -516,42 +424,25 @@ class NeoModel
                                parameter_names.add e.to_s
                        end
                end
-               var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
-               mentities[node] = mclass
+               var mclass = new MClass(mmodule, name, location, parameter_names, kind, visibility)
+               mentities[node.id.as(Int)] = mclass
                set_doc(node, mclass)
                return mclass
        end
 
-       # Build a `NeoNode` representing `mclassdef`.
-       private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
-               var node = make_node(mclassdef)
-               node.labels.add "MClassDef"
-               node["location"] = mclassdef.location.to_s
-               node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
-               node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
-               for mproperty in mclassdef.intro_mproperties do
-                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mproperty)))
-               end
-               for mpropdef in mclassdef.mpropdefs do
-                       node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mpropdef)))
-               end
-               for sup in mclassdef.supertypes do
-                       node.out_edges.add(new NeoEdge(node, "INHERITS", to_node(sup)))
-               end
-               return node
-       end
-
        # Build a new `MClassDef` from a `node`.
        #
        # 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
@@ -562,87 +453,46 @@ class NeoModel
                return mclassdef
        end
 
-       # Build a `NeoNode` representing `mproperty`.
-       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["is_init"] = mproperty.is_init
-               else if mproperty isa MAttribute then
-                       node.labels.add "MAttribute"
-               else if mproperty isa MVirtualTypeProp then
-                       node.labels.add "MVirtualTypeProp"
-               end
-               node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
-               return node
-       end
-
        # Build a new `MProperty` from a `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
+               var location = to_location(node["location"].to_s)
                var visibility = to_visibility(node["visibility"].to_s)
                var mprop: nullable MProperty = null
                if node.labels.has("MMethod") then
-                       mprop = new MMethod(intro_mclassdef, name, visibility)
+                       mprop = new MMethod(intro_mclassdef, name, location, visibility)
                        mprop.is_init = node["is_init"].as(Bool)
                else if node.labels.has("MAttribute") then
-                       mprop = new MAttribute(intro_mclassdef, name, visibility)
+                       mprop = new MAttribute(intro_mclassdef, name, location, visibility)
                else if node.labels.has("MVirtualTypeProp") then
-                       mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
+                       mprop = new MVirtualTypeProp(intro_mclassdef, name, location, visibility)
+               else if node.labels.has("MInnerClass") then
+                       var inner = to_mclass(model, node.out_nodes("NESTS").first)
+                       mprop = new MInnerClass(intro_mclassdef, name, location, 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
 
-       # Build a `NeoNode` representing `mpropdef`.
-       private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
-               var node = make_node(mpropdef)
-               node.labels.add "MPropDef"
-               node["location"] = mpropdef.location.to_s
-               node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
-               if mpropdef isa MMethodDef then
-                       node.labels.add "MMethodDef"
-                       node["is_abstract"] = mpropdef.is_abstract
-                       node["is_intern"] = mpropdef.is_intern
-                       node["is_extern"] = mpropdef.is_extern
-                       var msignature = mpropdef.msignature
-                       if msignature != null then
-                               node.out_edges.add(new NeoEdge(node, "SIGNATURE", to_node(msignature)))
-                       end
-               else if mpropdef isa MAttributeDef then
-                       node.labels.add "MAttributeDef"
-                       var static_mtype = mpropdef.static_mtype
-                       if static_mtype != null then
-                               node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype)))
-                       end
-               else if mpropdef isa MVirtualTypeDef then
-                       node.labels.add "MVirtualTypeDef"
-                       var bound = mpropdef.bound
-                       if bound != null then
-                               node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
-                       end
-               end
-               return node
-       end
-
        # Build a new `MPropDef` from a `node`.
        #
        # 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)
@@ -653,18 +503,23 @@ class NeoModel
                        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(",")}"
@@ -674,53 +529,13 @@ class NeoModel
                return mpropdef
        end
 
-       # Build a `NeoNode` representing `mtype`.
-       private fun mtype_node(mtype: MType): NeoNode do
-               var node = make_node(mtype)
-               node.labels.add "MType"
-               if mtype isa MClassType then
-                       node.labels.add "MClassType"
-                       node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
-                       for arg in mtype.arguments do
-                               node.out_edges.add(new NeoEdge(node, "ARGUMENT", to_node(arg)))
-                       end
-                       if mtype isa MGenericType then
-                               node.labels.add "MGenericType"
-                       end
-               else if mtype isa MVirtualType then
-                       node.labels.add "MVirtualType"
-                       node.out_edges.add(new NeoEdge(node, "PROPERTY", to_node(mtype.mproperty)))
-               else if mtype isa MParameterType then
-                       node.labels.add "MParameterType"
-                       node["rank"] = mtype.rank
-                       node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
-               else if mtype isa MNullableType then
-                       node.labels.add "MNullableType"
-                       node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype)))
-               else if mtype isa MSignature then
-                       node.labels.add "MSignature"
-                       var names = new JsonArray
-                       var rank = 0
-                       for mparameter in mtype.mparameters do
-                               names.add mparameter.name
-                               var pnode = to_node(mparameter)
-                               pnode["rank"] = rank
-                               node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
-                       end
-                       if not names.is_empty then node["parameter_names"] = names
-                       var return_mtype = mtype.return_mtype
-                       if return_mtype != null then
-                               node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
-                       end
-               end
-               return node
-       end
-
        # Build a new `MType` from a `node`.
        #
        # 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)
@@ -729,24 +544,24 @@ class NeoModel
                                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
@@ -770,51 +585,51 @@ class NeoModel
                                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)
+                       var parts = node["text"]
+                       if parts isa JsonArray then
+                               for p in parts do
+                                       mtype.parts.add(new MTypePart(model, p.to_s, null))
+                               end
+                               for pnode in node.out_nodes("LINK") do
+                                       assert pnode.labels.has("MTypePart")
+                                       if not pnode.out_nodes("TARGET").is_empty then
+                                               var rank = pnode["rank"]
+                                               var target = to_mentity(model, pnode.out_nodes("TARGET").first)
+                                               assert rank isa Int
+                                               mtype.parts[rank] = mtype.parts[rank].link_to(target)
+                                       end
+                               end
+                       end
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                end
                print "not yet implemented to_mtype for {node.labels.join(",")}"
                abort
        end
 
-       # Build a `NeoNode` representing `mparameter`.
-       private fun mparameter_node(mparameter: MParameter): NeoNode do
-               var node = make_node(mparameter)
-               node.labels.add "MParameter"
-               node["name"] = mparameter.name
-               node["is_vararg"] = mparameter.is_vararg
-               node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
-               return node
-       end
-
        # Build a new `MParameter` from `node`.
        #
        # 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)
+       private fun to_location(loc: String): nitc::Location do
+               return new nitc::Location.from_string(loc)
        end
 
        # Get a `MVisibility` from its string representation.
@@ -827,6 +642,8 @@ class NeoModel
                        return protected_visibility
                else if vis == private_visibility.to_s then
                        return private_visibility
+               else if vis == package_visibility.to_s then
+                       return package_visibility
                else
                        return none_visibility
                end
@@ -844,8 +661,9 @@ class NeoModel
                        return enum_kind
                else if kind == extern_kind.to_s then
                        return extern_kind
+               else
+                       return raw_kind(kind)
                end
-               abort
        end
 
        # Extract the `MDoc` from `node` and link it to `mentity`.
@@ -855,10 +673,342 @@ class NeoModel
                        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
                end
        end
 end
+
+redef class MPackage
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var root = root
+               if root != null then
+                       node.out_edges.add(new NeoEdge(node, "ROOT", root.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MGroup
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var parent = parent
+               node.out_edges.add(new NeoEdge(node, "PROJECT", mpackage.to_node(nodes, model_name)))
+               if parent != null then
+                       node.out_edges.add(new NeoEdge(node, "PARENT", parent.to_node(nodes, model_name)))
+               end
+               for mmodule in mmodules do
+                       node.out_edges.add(new NeoEdge(node, "DECLARES", mmodule.to_node(nodes, model_name)))
+               end
+               for subgroup in in_nesting.direct_smallers do
+                       node.in_edges.add(new NeoEdge(node, "NESTS", subgroup.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MModule
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               for parent in in_importation.direct_greaters do
+                       node.out_edges.add(new NeoEdge(node, "IMPORTS", parent.to_node(nodes, model_name)))
+               end
+               for mclass in intro_mclasses do
+                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", mclass.to_node(nodes, model_name)))
+               end
+               for mclassdef in mclassdefs do
+                       node.out_edges.add(new NeoEdge(node, "DEFINES", mclassdef.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MClass
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["kind"] = kind.to_s
+               node["visibility"] = visibility.to_s
+               if not mparameters.is_empty then
+                       var parameter_names = new Array[String]
+                       for p in mparameters do parameter_names.add(p.name)
+                       node["parameter_names"] = new JsonArray.from(parameter_names)
+               end
+               node.out_edges.add(new NeoEdge(node, "CLASSTYPE", mclass_type.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MClassDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", bound_mtype.to_node(nodes, model_name)))
+               node.out_edges.add(new NeoEdge(node, "MCLASS", mclass.to_node(nodes, model_name)))
+               for mproperty in intro_mproperties do
+                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", mproperty.to_node(nodes, model_name)))
+               end
+               for mpropdef in mpropdefs do
+                       node.out_edges.add(new NeoEdge(node, "DECLARES", mpropdef.to_node(nodes, model_name)))
+               end
+               for sup in supertypes do
+                       node.out_edges.add(new NeoEdge(node, "INHERITS", sup.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MProperty
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MProperty"
+               node["visibility"] = visibility.to_s
+               node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", intro_mclassdef.to_node(nodes, model_name)))
+               node.labels.add self.class_name
+               return node
+       end
+end
+
+redef class MMethod
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["is_init"] = is_init
+               return node
+       end
+end
+
+redef class MInnerClass
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MPropDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MPropDef"
+               node.out_edges.add(new NeoEdge(node, "DEFINES", mproperty.to_node(nodes, model_name)))
+               node.labels.add self.class_name
+               return node
+       end
+end
+
+redef class MMethodDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["is_abstract"] = is_abstract
+               node["is_intern"] = is_intern
+               node["is_extern"] = is_extern
+               var msignature = msignature
+               if msignature != null then
+                       node.out_edges.add(new NeoEdge(node, "SIGNATURE", msignature.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MAttributeDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var static_mtype = static_mtype
+               if static_mtype != null then
+                       node.out_edges.add(new NeoEdge(node, "TYPE", static_mtype.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MVirtualTypeDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var bound = bound
+               if bound != null then
+                       node.out_edges.add(new NeoEdge(node, "BOUND", bound.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MInnerClassDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MTypePart
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               if self.target != null then
+                       var target_node = self.target.as(not null).to_node(nodes, model_name)
+                       node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
+               end
+               return node
+       end
+end
+
+redef class MParameter
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["name"] = self.name
+               node["is_vararg"] = self.is_vararg
+               node.out_edges.add(new NeoEdge(node, "TYPE", self.mtype.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MClassType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MClassType"
+               node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
+               for arg in arguments do
+                       node.out_edges.add(new NeoEdge(node, "ARGUMENT", arg.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+
+redef class MGenericType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MGenericType"
+               return node
+       end
+end
+
+redef class MVirtualType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MVirtualType"
+               node.out_edges.add(new NeoEdge(node, "PROPERTY", mproperty.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MParameterType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MParameterType"
+               node["rank"] = rank
+               node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MNullableType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MNullableType"
+               node.out_edges.add(new NeoEdge(node, "TYPE", mtype.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MSignature
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MSignature"
+               var names = new JsonArray
+               var rank = 0
+               for mparameter in mparameters do
+                       names.add mparameter.name
+                       var pnode = mparameter.to_node(nodes, model_name)
+                       pnode["rank"] = rank
+                       node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
+                       rank += 1
+               end
+               if not names.is_empty then node["parameter_names"] = names
+               var return_mtype = return_mtype
+               if return_mtype != null then
+                       node.out_edges.add(new NeoEdge(node, "RETURNTYPE", return_mtype.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MRawType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MRawType"
+               var text = new JsonArray
+               var rank = 0
+               for part in self.parts do
+                       text.add part.text
+                       if part.target != null then
+                               var pnode = part.to_node(nodes, model_name)
+                               pnode["rank"] = rank
+                               node.out_edges.add(new NeoEdge(node, "LINK", pnode))
+                       end
+                       rank += 1
+               end
+               if not text.is_empty then node["text"] = text
+               return node
+       end
+end
+
+redef class MType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MType"
+               return node
+       end
+end
+
+redef class MEntity
+       # Build a `NeoNode` representing `self`.
+       private fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add self.class_name
+               return node
+       end
+
+       # Make a new `NeoNode` based on `mentity`.
+       private fun make_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               var node = new NeoNode
+               nodes[self] = node
+               node.labels.add "MEntity"
+               if model_name != null then node.labels.add model_name
+               node["name"] = self.name
+               if not self isa MSignature then
+                       #FIXME: MSignature is a MEntity, but has no model :/
+                       node["location"] = self.location.to_s
+               end
+               var mdoc = self.mdoc
+               if mdoc != null then
+                       node["mdoc"] = new JsonArray.from(mdoc.content)
+                       node["mdoc_location"] = mdoc.location.to_s
+               end
+               return node
+       end
+end