Save and load a Model to/from a Neo4j graph.

Nit models are composed by MEntities. This module creates NeoNode for each MEntity found in a Model and save them into Neo4j database.

SEE: Neo4jClient

NeoNodes can also be translated back to MEntities to rebuild a Nit Model.

Structure of the nit Model in the graph:

Note : Any null or empty attribute will not be saved in the database.

For any MEntity (in addition to specific data):

  • labels: model name (model_name) and MEntity.
  • name: short (unqualified) name.
  • mdoc: JSON array representing the associated Markdown documentation (one item by line).

Note : All nodes described here are MEntities.

MPackage

MGroup

  • labels: MGroup, model_name and MEntity.
  • (: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 this group.
  • (:MGroup)-[:NESTS]->(:MGroup): nested groups that are direct children of this group.

MModule

  • labels: MModule, model_name and MEntity.
  • 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 module.
  • (:MModule)-[:DEFINES]->(:MClassDef): all class definitons contained in this module.

MClass

  • labels: MClass, model_name and MEntity.
  • 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 parameter (in order of declaration).
  • (:MClass)-[:CLASSTYPE]->(:MClassType): SEE: MClass.mclass_type

Arguments in the CLASSTYPE are named following the parameter_names attribute of the MClassDef that introduces the class. A class definition introduces a class if and only if it has this class as MCLASS and has is_intro set to true.

MClassDef

  • labels: MClassDef, model_name and MEntity.
  • location: origin of the definition. SEE: Location.to_s
  • (:MClassDef)-[:BOUNDTYPE]->(:MClassType): bounded type associated to the classdef.
  • (:MClassDef)-[:MCLASS]->(:MClass): associated MClass.
  • (:MClassDef)-[:INTRODUCES]->(:MProperty): all properties introduced by the classdef.
  • (:MClassDef)-[:DECLARES]->(:MPropDef): all property definitions in the classdef (introductions and redefinitions).
  • (:MClassDef)-[:INHERITS]->(:MClassType): all declared super-types

MProperty

Additional relationship for MInnerClass:

  • (:MInnerClassDef)-[:NESTS]->(:MClass): actual inner class.

MPropDef

Additional attributes and relationship for MMethodDef:

  • is_abstract: Is the method definition abstract?
  • is_intern: Is the method definition intern?
  • is_extern: Is the method definition extern?
  • (:MMethodDef)-[:SIGNATURE]->(:MSignature): signature attached to the property definition.

Additional relationship for MAttributeDef:

  • (:MAttributeDef)-[:TYPE]->(:MType): static type of the attribute, if specified.

Additional relationship for MVirtualTypeDef:

  • (:MVirtualTypeDef)-[:BOUND]->(:MType): type to which the virtual type 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

Additional label and relationships for MClassType:

  • If it is a MGenericType, also has the MGenericType label.
  • (:MClassType)-[:CLASS]->(:MClass): SEE: MClassType.mclass
  • (:MClassType)-[:ARGUMENT]->(:MType): type arguments.

Arguments are named following the parameter_names attribute of the MClass referred by CLASS.

Additional relationship for MVirtualType:

  • (:MVirtualType)-[:PROPERTY]->(:MProperty): associated property that determines the type (usually a MVirtualTypeProp).

Additional attribute and relationship for MParameterType:

  • rank: position of the parameter (0 for the first parameter).
  • (:MParameterType)-[:CLASS]->(:MClass): generic class where the parameter belong.

Additional relationship for MNullableType:

  • (: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.
  • (:MSignature)-[:PARAMETER]->(:MParameter): parameters.
  • (:MSignature)-[:RETURNTYPE]->(:MType): return type. Does not exist for procedures.

In order to maintain the correct parameters order, each MSignature node contains an array of parameter names corresponding to the parameter order in the signature.

For example, if the source code contains:

    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 with the parameters in the correct order.

MParameter

  • labels: MParameter, model_name and MEntity.
  • is_vararg: Is the parameter a vararg?
  • rank: position of the parameter (0 for the first parameter).
  • (:MParameter)-[:TYPE]->(:MType): static type of the parameter.

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

Introduced classes

class NeoModel

nitc :: NeoModel

Helper class that can save and load a Model into a Neo4j database.

Redefined classes

redef class MAttributeDef

nitc :: neo $ MAttributeDef

A local definition of an attribute
redef class MClass

nitc :: neo $ MClass

A named class
redef class MClassDef

nitc :: neo $ MClassDef

A definition (an introduction or a refinement) of a class in a module
redef class MClassType

nitc :: neo $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: neo $ MEntity

A named and possibly documented entity in the model.
redef class MGenericType

nitc :: neo $ MGenericType

A type based on a generic class.
redef class MGroup

nitc :: neo $ MGroup

A group of modules in a package
redef class MInnerClass

nitc :: neo $ MInnerClass

An inner class.
redef class MInnerClassDef

nitc :: neo $ MInnerClassDef

An inner class definition.
redef class MMethod

nitc :: neo $ MMethod

A global method
redef class MMethodDef

nitc :: neo $ MMethodDef

A local definition of a method
redef class MModule

nitc :: neo $ MModule

A Nit module is usually associated with a Nit source file.
redef class MNullableType

nitc :: neo $ MNullableType

A type prefixed with "nullable"
redef class MPackage

nitc :: neo $ MPackage

A Nit package, that encompass a product
redef class MParameter

nitc :: neo $ MParameter

A parameter in a signature
redef class MParameterType

nitc :: neo $ MParameterType

The type associated to a formal parameter generic type of a class
redef abstract class MPropDef

nitc :: neo $ MPropDef

A definition of a property (local property)
redef abstract class MProperty

nitc :: neo $ MProperty

A service (global property) that generalize method, attribute, etc.
redef class MRawType

nitc :: neo $ MRawType

A type described by a text annoted with links.
redef class MSignature

nitc :: neo $ MSignature

A signature of a method
redef abstract class MType

nitc :: neo $ MType

A global static type
redef class MTypePart

nitc :: neo $ MTypePart

A part of a RawType.
redef class MVirtualType

nitc :: neo $ MVirtualType

A virtual formal type.
redef class MVirtualTypeDef

nitc :: neo $ MVirtualTypeDef

A local definition of a virtual type

All class definitions

redef class MAttributeDef

nitc :: neo $ MAttributeDef

A local definition of an attribute
redef class MClass

nitc :: neo $ MClass

A named class
redef class MClassDef

nitc :: neo $ MClassDef

A definition (an introduction or a refinement) of a class in a module
redef class MClassType

nitc :: neo $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: neo $ MEntity

A named and possibly documented entity in the model.
redef class MGenericType

nitc :: neo $ MGenericType

A type based on a generic class.
redef class MGroup

nitc :: neo $ MGroup

A group of modules in a package
redef class MInnerClass

nitc :: neo $ MInnerClass

An inner class.
redef class MInnerClassDef

nitc :: neo $ MInnerClassDef

An inner class definition.
redef class MMethod

nitc :: neo $ MMethod

A global method
redef class MMethodDef

nitc :: neo $ MMethodDef

A local definition of a method
redef class MModule

nitc :: neo $ MModule

A Nit module is usually associated with a Nit source file.
redef class MNullableType

nitc :: neo $ MNullableType

A type prefixed with "nullable"
redef class MPackage

nitc :: neo $ MPackage

A Nit package, that encompass a product
redef class MParameter

nitc :: neo $ MParameter

A parameter in a signature
redef class MParameterType

nitc :: neo $ MParameterType

The type associated to a formal parameter generic type of a class
redef abstract class MPropDef

nitc :: neo $ MPropDef

A definition of a property (local property)
redef abstract class MProperty

nitc :: neo $ MProperty

A service (global property) that generalize method, attribute, etc.
redef class MRawType

nitc :: neo $ MRawType

A type described by a text annoted with links.
redef class MSignature

nitc :: neo $ MSignature

A signature of a method
redef abstract class MType

nitc :: neo $ MType

A global static type
redef class MTypePart

nitc :: neo $ MTypePart

A part of a RawType.
redef class MVirtualType

nitc :: neo $ MVirtualType

A virtual formal type.
redef class MVirtualTypeDef

nitc :: neo $ MVirtualTypeDef

A local definition of a virtual type
class NeoModel

nitc $ NeoModel

Helper class that can save and load a Model into a Neo4j database.
package_diagram nitc::neo neo nitc::model_ext model_ext nitc::neo->nitc::model_ext neo4j neo4j nitc::neo->neo4j nitc::toolcontext toolcontext nitc::neo->nitc::toolcontext nitc\>model\> model nitc::model_ext->nitc\>model\> json json neo4j->json curl curl neo4j->curl progression progression neo4j->progression pipeline pipeline neo4j->pipeline opts opts nitc::toolcontext->opts nitc::location location nitc::toolcontext->nitc::location nitc::version version nitc::toolcontext->nitc::version template template nitc::toolcontext->template more_collections more_collections nitc::toolcontext->more_collections ...nitc\>model\> ... ...nitc\>model\>->nitc\>model\> ...json ... ...json->json ...curl ... ...curl->curl ...progression ... ...progression->progression ...pipeline ... ...pipeline->pipeline ...opts ... ...opts->opts ...nitc::location ... ...nitc::location->nitc::location ...nitc::version ... ...nitc::version->nitc::version ...template ... ...template->template ...more_collections ... ...more_collections->more_collections nitc::test_neo test_neo nitc::test_neo->nitc::neo a_star-m a_star-m a_star-m->nitc::test_neo a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module curl

curl :: curl

Data transfer powered by the native curl library
module curl_json

neo4j :: curl_json

cURL requests compatible with the JSON REST APIs.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

json :: error

Intro JsonParseError which is exposed by all JSON reading APIs
module error

core :: error

Standard error-management infrastructure.
module error

neo4j :: error

Errors thrown by the neo4j library.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module json

json :: json

Read and write JSON formatted text using the standard serialization services
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module native_curl

curl :: native_curl

Binding of C libCurl which allow us to interact with network.
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser_base

parser_base :: parser_base

Simple base for hand-made parsers of all kinds
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module safe

serialization :: safe

Services for safer deserialization engines
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module serialization_read

json :: serialization_read

Services to read JSON: deserialize_json and JsonDeserializer
module serialization_write

json :: serialization_write

Services to write Nit objects to JSON strings: serialize_to_json and JsonSerializer
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module static

json :: static

Static interface to read Nit objects from JSON strings
module stream

core :: stream

Input and output streams of characters
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module model_ext

nitc :: model_ext

Extensions to the Nit model for foreign languages.
module neo4j

neo4j :: neo4j

Neo4j connector through its JSON REST API using curl.
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages

Children

module test_neo

nitc :: test_neo

Test for neo model saving and loading.

Descendants

module a_star-m

a_star-m

# Save and load a `Model` to/from a Neo4j graph.
#
# Nit models are composed by MEntities.
# This module creates NeoNode for each MEntity found in a `Model` and save them
# into Neo4j database.
#
# SEE: `Neo4jClient`
#
# NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
#
# Structure of the nit `Model` in the graph:
#
# Note : Any null or empty attribute will not be saved in the database.
#
# For any `MEntity` (in addition to specific data):
#
# * labels: model name (`model_name`) and `MEntity`.
# * `name`: short (unqualified) name.
# * `mdoc`: JSON array representing the associated Markdown documentation
# (one item by line).
#
# Note : All nodes described here are MEntities.
#
# `MPackage`
#
# * labels: `MPackage`, `model_name` and `MEntity`.
# * `(:MPackage)-[:ROOT]->(:MGroup)`: root of the group tree.
#
# `MGroup`
#
# * labels: `MGroup`, `model_name` and `MEntity`.
# * `(: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
# this group.
# * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
# this group.
#
# `MModule`
#
# * labels: `MModule`, `model_name` and `MEntity`.
# * `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
# module.
# * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
# this module.
#
# `MClass`
#
# * labels: `MClass`, `model_name` and `MEntity`.
# * `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
# parameter (in order of declaration).
# * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
#
# Arguments in the `CLASSTYPE` are named following the `parameter_names`
# attribute of the `MClassDef` that introduces the class. A class definition
# introduces a class if and only if it has this class as `MCLASS` and
# has `is_intro` set to `true`.
#
# `MClassDef`
#
# * labels: `MClassDef`, `model_name` and `MEntity`.
# * `location`: origin of the definition. SEE: `Location.to_s`
# * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
# classdef.
# * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
# * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
# the classdef.
# * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
# classdef (introductions and redefinitions).
# * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
#
# `MProperty`
#
# * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
# `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`, `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.
#
# Additional attributes and relationship for `MMethodDef`:
#
# * `is_abstract`: Is the method definition abstract?
# * `is_intern`: Is the method definition intern?
# * `is_extern`: Is the method definition extern?
# * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
# property definition.
#
# Additional relationship for `MAttributeDef`:
#
# * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
# if specified.
#
# Additional relationship for `MVirtualTypeDef`:
#
# * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
# 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`, `MRawType` or `MSignature`, depending on the
# class of the represented entity.
#
# Additional label and relationships for `MClassType`:
#
# * If it is a `MGenericType`, also has the `MGenericType` label.
# * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
# * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
#
# Arguments are named following the `parameter_names` attribute of the
# `MClass` referred by `CLASS`.
#
# Additional relationship for `MVirtualType`:
#
# * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
# determines the type (usually a `MVirtualTypeProp`).
#
# Additional attribute and relationship for `MParameterType`:
#
# * `rank`: position of the parameter (0 for the first parameter).
# * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
# belong.
#
# Additional relationship for `MNullableType`:
#
# * `(: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.
# * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
# * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
# procedures.
#
# In order to maintain the correct parameters order, each `MSignature` node
# contains an array of parameter names corresponding to the parameter order in
# the signature.
#
# 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
# with the parameters in the correct order.
#
# `MParameter`
#
# * labels: `MParameter`, `model_name` and `MEntity`.
# * `is_vararg`: Is the parameter a vararg?
# * `rank`: position of the parameter (0 for the first parameter).
# * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
#
# 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 doc::model_ext
import neo4j
import toolcontext

# Helper class that can save and load a `Model` into a Neo4j database.
class NeoModel

	# The model name.
	#
	# Because we use only one Neo4j instance to store all the models,
	# we need to mark their appartenance to a particular model and avoid loading all models.
	#
	# The name is used as a Neo label on each created nodes and used to load nodes from base.
	var model_name: String

	# The toolcontext used to init the `NeoModel` tool.
	var toolcontext: ToolContext

	# The Neo4j `client` used to communicate with the Neo4j instance.
	var client: Neo4jClient

	# Fill `model` using base pointed by `client`.
	fun load(model: Model): Model do
		var nodes: Array[NeoNode]

		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
	end

	# Save `model` in the base pointed by `client`.
	fun save(model: Model) do
		var nodes = collect_model_nodes(model)
		toolcontext.info("Save {nodes.length} nodes...", 1)
		push_all(nodes)
		var edges = collect_model_edges(model)
		toolcontext.info("Save {edges.length} edges...", 1)
		push_all(edges)
	end

	# Save `neo_entities` in base using batch mode.
	private fun push_all(neo_entities: Collection[NeoEntity]) do
		var batch = new NeoBatch(client)
		var len = neo_entities.length
		var sum = 0
		var i = 1
		for nentity in neo_entities do
			batch.save_entity(nentity)
			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

	# Execute `batch` and check for errors.
	#
	# Abort if `batch.execute` returns errors.
	private fun do_batch(batch: NeoBatch) do
		var errors = batch.execute
		if not errors.is_empty then
			print errors
			exit(1)
		end
	end

	# Collect all nodes from the current `model`.
	private fun collect_model_nodes(model: Model): Collection[NeoNode] do
		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

	# Collect all edges from the current `model`.
	#
	# Actually collect all out_edges from all nodes.
	private fun collect_model_edges(model: Model): Collection[NeoEdge] do
		var edges = new HashSet[NeoEdge]
		for node in nodes.values do edges.add_all(node.out_edges)
		return edges
	end

	# Mentities associated to nodes.
	#
	# 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 `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

	# Build a new `MPackage` from a `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
		var m = mentities.get_or_null(node.id.as(Int))
		if m isa MGroup then return m

		assert node.labels.has("MGroup")
		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, location, mpackage, parent)
		mentities[node.id.as(Int)] = mgroup
		set_doc(node, mgroup)
		return mgroup
	end

	# Build a new `MModule` from a `node`.
	#
	# REQUIRE `node.labels.has("MModule")`
	private fun to_mmodule(model: Model, node: NeoNode): MModule do
		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
		if not ins.is_empty then
			mgroup = to_mgroup(model, ins.first)
		end
		var name = node["name"].to_s
		var location = to_location(node["location"].to_s)
		var mmodule = new MModule(model, mgroup, name, location)
		mentities[node.id.as(Int)] = mmodule
		set_doc(node, mmodule)
		var imported_mmodules = new Array[MModule]
		for smod in node.out_nodes("IMPORTS") do
			imported_mmodules.add to_mmodule(model, smod)
		end
		mmodule.set_imported_mmodules(imported_mmodules)
		return mmodule
	end

	# Build a new `MClass` from a `node`.
	#
	# REQUIRE `node.labels.has("MClass")`
	private fun to_mclass(model: Model, node: NeoNode): MClass do
		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]
		if node.has_key("parameter_names") then
			for e in node["parameter_names"].as(JsonArray) do
				parameter_names.add e.to_s
			end
		end
		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 new `MClassDef` from a `node`.
	#
	# REQUIRE `node.labels.has("MClassDef")`
	private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
		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.id.as(Int)] = mclassdef
		set_doc(node, mclassdef)
		var supertypes = new Array[MClassType]
		for sup in node.out_nodes("INHERITS") do
			supertypes.add to_mtype(model, sup).as(MClassType)
		end
		mclassdef.set_supertypes(supertypes)
		mclassdef.add_in_hierarchy
		return mclassdef
	end

	# Build a new `MProperty` from a `node`.
	#
	# REQUIRE `node.labels.has("MProperty")`
	private fun to_mproperty(model: Model, node: NeoNode): MProperty do
		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, location, visibility)
			mprop.is_init = node["is_init"].as(Bool)
		else if node.labels.has("MAttribute") then
			mprop = new MAttribute(intro_mclassdef, name, location, visibility)
		else if node.labels.has("MVirtualTypeProp") then
			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.id.as(Int)] = mprop
		set_doc(node, mprop)
		return mprop
	end

	# Build a new `MPropDef` from a `node`.
	#
	# REQUIRE `node.labels.has("MPropDef")`
	private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
		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)
		var location = to_location(node["location"].to_s)
		var mpropdef: nullable MPropDef = null
		if node.labels.has("MMethodDef") then
			mpropdef = new MMethodDef(mclassdef, mproperty.as(MMethod), location)
			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.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.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.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(",")}"
			abort
		end
		set_doc(node, mpropdef)
		return mpropdef
	end

	# Build a new `MType` from a `node`.
	#
	# REQUIRE `node.labels.has("MType")`
	private fun to_mtype(model: Model, node: NeoNode): MType do
		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)
			var args = new Array[MType]
			for narg in node.out_nodes("ARGUMENT") do
				args.add to_mtype(model, narg)
			end
			var mtype = mclass.get_mtype(args)
			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.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.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.id.as(Int)] = mtype
			return mtype
		else if node.labels.has("MSignature") then
			# Get all param nodes
			var mparam_nodes = new HashMap[String, MParameter]
			for pnode in node.out_nodes("PARAMETER") do
				var mparam = to_mparameter(model, pnode)
				mparam_nodes[mparam.name] = mparam
			end
			# Load params in the good order
			var mparam_names = node["parameter_names"]
			var mparameters = new Array[MParameter]
			if mparam_names isa JsonArray then
				for mparam_name in mparam_names do
					var mparam = mparam_nodes[mparam_name.to_s]
					mparameters.add mparam
				end
			end
			var return_mtype: nullable MType = null
			var ret_nodes = node.out_nodes("RETURNTYPE")
			if not ret_nodes.is_empty then
				return_mtype = to_mtype(model, ret_nodes.first)
			end
			var mtype = new MSignature(mparameters, return_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 new `MParameter` from `node`.
	#
	# REQUIRE `node.labels.has("MParameter")`
	private fun to_mparameter(model: Model, node: NeoNode): MParameter do
		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.id.as(Int)] = mparameter
		return mparameter
	end

	# Get a `Location` from its string representation.
	private fun to_location(loc: String): nitc::Location do
		return new nitc::Location.from_string(loc)
	end

	# Get a `MVisibility` from its string representation.
	private fun to_visibility(vis: String): MVisibility do
		if vis == intrude_visibility.to_s then
			return intrude_visibility
		else if vis == public_visibility.to_s then
			return public_visibility
		else if vis == protected_visibility.to_s then
			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
	end

	# Get a `MKind` from its string representation.
	private fun to_kind(kind: String): MClassKind do
		if kind == abstract_kind.to_s then
			return abstract_kind
		else if kind == concrete_kind.to_s then
			return concrete_kind
		else if kind == interface_kind.to_s then
			return interface_kind
		else if kind == enum_kind.to_s then
			return enum_kind
		else if kind == extern_kind.to_s then
			return extern_kind
		else
			return raw_kind(kind)
		end
	end

	# Extract the `MDoc` from `node` and link it to `mentity`.
	private fun set_doc(node: NeoNode, mentity: MEntity) do
		if node.has_key("mdoc") then
			var lines = new Array[String]
			for e in node["mdoc"].as(JsonArray) do
				lines.add e.to_s#.replace("\n", "\\n")
			end
			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
src/neo.nit:15,1--1014,3