tests: error_syntax errors on `? now
[nit.git] / contrib / neo_doxygen / src / model / graph.nit
index 98244eb..9df491a 100644 (file)
@@ -16,7 +16,9 @@
 module model::graph
 
 import neo4j
+import more_collections
 import location
+import descriptions
 
 # A Neo4j graph.
 class NeoGraph
@@ -35,6 +37,22 @@ class NeoGraph
 end
 
 # A project’s graph.
+#
+# Here is the usual steps to build a project graph:
+#
+# <ul>
+# <li>Instantiate `ProjectGraph` by giving the name that will label the project.</li>
+# <li>For each compound:
+# <ul>
+# <li>Instantiate the compound.</li>
+# <li>Provide all the related data.</li>
+# <li>Call the `put_in_graph` method of the compound.</li>
+# </ul></li>
+# <li>Call the `add_global_modules` method of the project’s graph (defined in
+# the `module_compound` module). This permits to take global classes into
+# account correctly.</li>
+# <li>Call the `put_edges` method of the project’s graph.</li>
+# </ul>
 class ProjectGraph
        super NeoGraph
 
@@ -49,6 +67,21 @@ class ProjectGraph
        # Entities by `model_id`.
        var by_id: Map[String, Entity] = new HashMap[String, Entity]
 
+       # Namespaces by `full_name`.
+       var namespaces: Map[String, Namespace] = new HashMap[String, Namespace]
+
+       # For each `ClassCompound` in the graph, the mapping between its `model_id` and its namespace.
+       #
+       # Defaults to the root namespace. An entry is added each time
+       # `Namespace.declare_class` is called.
+       #
+       # Note: In the graph, there is no direct link between a namespace and a
+       # class. It is the role of a module (created internally by a `FileCompound`)
+       # to link a class with its namespace. So, this collection is used by modules
+       # to know which class in a file belong to their related namespace. It is
+       # also used by `FileCompound` to detect classes in the root namespace.
+       var class_to_ns: Map[String, Namespace] is noinit
+
        # Initialize a new project graph using the specified project name.
        #
        # The specified name will label all nodes of the project’s graph.
@@ -62,9 +95,15 @@ class ProjectGraph
                var root = new RootNamespace(self)
                root.put_in_graph
                by_id[""] = root
+               class_to_ns = new DefaultMap[String, Namespace](root)
        end
 
        # Request to all nodes in the graph to add their related edges.
+       #
+       # Note: For the rare cases where a node need to wait the `put_edges` to add
+       # an implicit node, this method makes sure to call the `put_edges` method
+       # of the newly added nodes only after processing all the nodes that was
+       # already there.
        fun put_edges do
                all_edges.clear
                add_edge(project, "ROOT", by_id[""])
@@ -90,8 +129,14 @@ abstract class Entity
        # Is empty for entities without an ID.
        var model_id: String = "" is writable
 
+       # The full (qualified) name, as presented by the original model.
+       #
+       # Fully independant of `name`. By default, equals to `""` for the root
+       # namespace.
+       var full_name: nullable String = null is writable
+
        # Associated documentation.
-       var doc = new JsonArray is writable
+       var doc = new Documentation is writable
 
        init do
                self.labels.add(graph.project_name)
@@ -99,8 +144,6 @@ abstract class Entity
        end
 
        # The short (unqualified) name.
-       #
-       # May be also set by `full_name=`.
        fun name=(name: String) do
                self["name"] = name
        end
@@ -118,47 +161,26 @@ abstract class Entity
        end
 
        # The namespace separator of Nit/C++.
-       fun ns_separator: String do return "::"
-
-       # The name separator used when calling `full_name=`.
-       fun name_separator: String do return ns_separator
-
-       # The full (qualified) name.
        #
-       # Also set `name` using `name_separator`.
-       fun full_name=(full_name: String) do
-               var m: nullable Match = full_name.search_last(name_separator)
-
-               self["full_name"] = full_name
-               if m == null then
-                       name = full_name
-               else
-                       name = full_name.substring_from(m.after)
-               end
-       end
-
-       # The full (qualified) name.
-       fun full_name: String do
-               var full_name = self["full_name"]
-               assert full_name isa String
-               return full_name
-       end
-
-       # Set the full name using the current name and the specified parent name.
-       fun parent_name=(parent_name: String) do
-               self["full_name"] = parent_name + name_separator + self["name"].as(not null).to_s
-       end
+       # Used to join two or more names when we need to work around some
+       # limitations of the Nit model.
+       fun ns_separator: String do return "::"
 
        # Set the location of the entity in the source code.
-       fun location=(location: nullable Location) do
+       fun location=(location: nullable neo_doxygen::Location) do
                self["location"] = location
        end
 
+       # Get the location of the entity in the source code.
+       fun location: nullable neo_doxygen::Location do
+               return self["location"].as(nullable neo_doxygen::Location)
+       end
+
        # Put the entity in the graph.
        #
        # Called by the loader when it has finished to read the entity.
        fun put_in_graph do
-               if doc.length > 0 then
+               if not doc.is_empty then
                        set_mdoc
                end
                graph.all_nodes.add(self)
@@ -180,12 +202,12 @@ abstract class CodeBlock
        super Entity
 
        init do
-               self["location"] = new Location
+               self["location"] = new neo_doxygen::Location
        end
 
-       redef fun location=(location: nullable Location) do
+       redef fun location=(location) do
                if location == null then
-                       super(new Location)
+                       super(new neo_doxygen::Location)
                else
                        super
                end
@@ -199,7 +221,7 @@ end
 abstract class Compound
        super Entity
 
-       # Set the declared visibility (the proctection) of the compound.
+       # Set the declared visibility (the protection) of the compound.
        fun visibility=(visibility: String) do
                self["visibility"] = visibility
        end
@@ -211,26 +233,28 @@ abstract class Compound
 
        # Declare an inner namespace.
        #
-       # Note: Althought Doxygen indicates that the name is optional,
-       # declarations with an empty name are not supported yet.
+       # Note: Although Doxygen indicates that the name is optional,
+       # declarations with an empty name are not supported yet, except for the root
+       # namespace. For the root namespace, both arguments are empty.
        #
        # Parameters:
        #
        # * `id`: `model_id` of the inner namespace. May be empty.
-       # * `full_name`: qualified name of the inner namespace.
+       # * `full_name`: qualified name of the inner namespace. Use an empty name
+       # for the root namespace.
        fun declare_namespace(id: String, full_name: String) do end
 
        # Declare an inner class.
        #
-       # Note: Althought Doxygen indicates that both arguments are optional,
-       # declarations with either an empty name or an empty ID are not
-       # supported yet.
+       # Note: Although Doxygen indicates that both arguments are optional,
+       # declarations with an empty ID are not supported yet.
        #
        # Parameters:
        #
        # * `id`: `model_id` of the inner class.
-       # * `full_name`: qualified name of the inner class.
-       fun declare_class(id: String, full_name: String) do end
+       # * `name`: short name of the inner class.
+       # * `prot`: visibility (proctection).
+       fun declare_class(id: String, name: String, prot: String) do end
 
        # Declare a base compound (usually, a base class).
        #
@@ -260,37 +284,71 @@ end
 class Namespace
        super Compound
 
-       # Inner namespaces (IDs).
-       #
-       # Left empty for the root namespace.
-       var inner_namespaces: SimpleCollection[String] = new Array[String]
+       # The inner namespaces.
+       var inner_namespaces: SimpleCollection[NamespaceRef] = new Array[NamespaceRef]
 
        init do
                super
                self.labels.add("MGroup")
        end
 
-       redef fun declare_namespace(id: String, name: String) do
-               inner_namespaces.add(id)
+       redef fun declare_namespace(id, full_name) do
+               inner_namespaces.add new NamespaceRef(id, full_name)
+       end
+
+       redef fun declare_class(id, name, prot) do
+               assert not id.is_empty else
+                       sys.stderr.write "Inner class declarations without ID are not yet supported.\n"
+               end
+               graph.class_to_ns[id] = self
+       end
+
+       redef fun put_in_graph do
+               super
+               var full_name = self.full_name
+               if full_name isa String then graph.namespaces[full_name] = self
        end
 
        redef fun put_edges do
                super
                graph.add_edge(self, "PROJECT", graph.project)
-               if self["name"] == self["full_name"] and self["full_name"] != "" then
-                       # The root namespace does not know its children.
-                       var root = graph.by_id[""]
-                       graph.add_edge(self, "PARENT", root)
-                       graph.add_edge(root, "NESTS", self)
-               end
                for ns in inner_namespaces do
-                       var node = graph.by_id[ns]
+                       var node = ns.seek_in(graph)
                        graph.add_edge(node, "PARENT", self)
                        graph.add_edge(self, "NESTS", node)
                end
        end
 end
 
+# A reference to a namespace.
+class NamespaceRef
+       # The `model_id` of the target.
+       #
+       # Empty when unknown or for the root namespace.
+       var model_id: String
+
+       # The `full_name` of the target.
+       #
+       # Empty only for the root namespace.
+       var full_name: String
+
+       # Look for the targeted namespace in the specified graph.
+       fun seek_in(graph: ProjectGraph): Namespace do
+               var ns_compound: Namespace
+
+               if model_id.is_empty and not full_name.is_empty then
+                       # ID unspecified. => We have to look by name
+                       assert graph.namespaces.has_key(full_name) else
+                               sys.stderr.write "Namespace `{full_name}` not found."
+                       end
+                       ns_compound = graph.namespaces[full_name]
+               else
+                       ns_compound = graph.by_id[model_id].as(Namespace)
+               end
+               return ns_compound
+       end
+end
+
 # The root namespace of a `ProjectGraph`.
 #
 # This the only entity in the graph whose `model_id` is really `""`.
@@ -300,9 +358,7 @@ class RootNamespace
 
        init do
                super
-               self["full_name"] = ""
-               self["name"] = graph.project["name"]
+               full_name = ""
+               name = graph.project_name
        end
-
-       redef fun declare_namespace(id: String, name: String) do end
 end