18f91c44a321b067acade4c7139126139fdf4b34
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Graphs and basic entities.
19 import more_collections
24 # All the nodes in the graph.
25 var all_nodes
: SimpleCollection[NeoNode] = new Array[NeoNode]
27 # All the edges in the graph.
28 var all_edges
: SimpleCollection[NeoEdge] = new Array[NeoEdge]
30 # Add a relationship between two nodes.
32 # Parameters are the same than for the constructor of `NeoEdge`.
33 fun add_edge
(from
: NeoNode, rel_type
: String, to
: NeoNode) do
34 all_edges
.add
(new NeoEdge(from
, rel_type
, to
))
40 # Here is the usual steps to build a project graph:
43 # <li>Instantiate `ProjectGraph` by giving the name that will label the project.</li>
44 # <li>For each compound:
46 # <li>Instantiate the compound.</li>
47 # <li>Provide all the related data.</li>
48 # <li>Call the `put_in_graph` method of the compound.</li>
50 # <li>Call the `add_global_modules` method of the project’s graph (defined in
51 # the `module_compound` module). This permits to take global classes into
52 # account correctly.</li>
53 # <li>Call the `put_edges` method of the project’s graph.</li>
59 var project_name
: String
61 # The node reperesenting the project.
63 # Once the project’s graph is initialized, this node must not be edited.
64 var project
= new NeoNode
66 # Entities by `model_id`.
67 var by_id
: Map[String, Entity] = new HashMap[String, Entity]
69 # Namespaces by `full_name`.
70 var namespaces
: Map[String, Namespace] = new HashMap[String, Namespace]
72 # For each `ClassCompound` in the graph, the mapping between its `model_id` and its namespace.
74 # Defaults to the root namespace. An entry is added each time
75 # `Namespace.declare_class` is called.
77 # Note: In the graph, there is no direct link between a namespace and a
78 # class. It is the role of a module (created internally by a `FileCompound`)
79 # to link a class with its namespace. So, this collection is used by modules
80 # to know which class in a file belong to their related namespace. It is
81 # also used by `FileCompound` to detect classes in the root namespace.
82 var class_to_ns
: Map[String, Namespace] is noinit
84 # Initialize a new project graph using the specified project name.
86 # The specified name will label all nodes of the project’s graph.
88 project
.labels
.add
(project_name
)
89 project
.labels
.add
("MEntity")
90 project
.labels
.add
("MProject")
91 project
["name"] = project_name
92 all_nodes
.add
(project
)
94 var root
= new RootNamespace(self)
97 class_to_ns
= new DefaultMap[String, Namespace](root
)
100 # Request to all nodes in the graph to add their related edges.
102 # Note: For the rare cases where a node need to wait the `put_edges` to add
103 # an implicit node, this method makes sure to call the `put_edges` method
104 # of the newly added nodes only after processing all the nodes that was
108 add_edge
(project
, "ROOT", by_id
[""])
109 for n
in all_nodes
do
119 # In practice, this is the base class of every node in a `ProjectGraph`.
120 abstract class Entity
123 # Graph that will embed the entity.
124 var graph
: ProjectGraph
126 # ID of the entity in the model.
128 # Is empty for entities without an ID.
129 var model_id
: String = "" is writable
131 # Associated documentation.
132 var doc
= new JsonArray is writable
135 self.labels
.add
(graph
.project_name
)
136 self.labels
.add
("MEntity")
139 # The short (unqualified) name.
141 # May be also set by `full_name=`.
142 fun name
=(name
: String) do
146 # The short (unqualified) name.
148 var name
= self["name"]
149 assert name
isa String
153 # Include the documentation of `self` in the graph.
154 protected fun set_mdoc
do
158 # The namespace separator of Nit/C++.
159 fun ns_separator
: String do return "::"
161 # The name separator used when calling `full_name=`.
162 fun name_separator
: String do return ns_separator
164 # The full (qualified) name.
166 # Also set `name` using `name_separator`.
167 fun full_name
=(full_name
: String) do
168 var m
= full_name
.search_last
(name_separator
)
170 self["full_name"] = full_name
174 name
= full_name
.substring_from
(m
.after
)
178 # The full (qualified) name.
179 fun full_name
: String do
180 var full_name
= self["full_name"]
181 assert full_name
isa String
185 # Set the full name using the current name and the specified parent name.
186 fun parent_name
=(parent_name
: String) do
187 if parent_name
.is_empty
then
188 self["full_name"] = name
190 self["full_name"] = parent_name
+ name_separator
+ name
194 # Set the location of the entity in the source code.
195 fun location
=(location
: nullable Location) do
196 self["location"] = location
199 # Get the location of the entity in the source code.
200 fun location
: nullable Location do
201 return self["location"].as(nullable Location)
204 # Put the entity in the graph.
206 # Called by the loader when it has finished to read the entity.
208 if doc
.length
> 0 then
211 graph
.all_nodes
.add
(self)
212 if model_id
!= "" then graph
.by_id
[model_id
] = self
215 # Put the related edges in the graph.
217 # This method is called on each node by `ProjectGraph.put_edges`.
219 # Note: Even at this step, the entity may modify its own attributes and
220 # inner entities’ ones because some values are only known once the entity
221 # know its relationships with the rest of the graph.
225 # An entity whose the location is mandatory.
226 abstract class CodeBlock
230 self["location"] = new Location
233 redef fun location
=(location
: nullable Location) do
234 if location
== null then
244 # Usually corresponds to a `<compounddef>` element in of the XML output of
246 abstract class Compound
249 # Set the declared visibility (the proctection) of the compound.
250 fun visibility
=(visibility
: String) do
251 self["visibility"] = visibility
254 # Set the specific kind of the compound.
255 fun kind
=(kind
: String) do
259 # Declare an inner namespace.
261 # Note: Althought Doxygen indicates that the name is optional,
262 # declarations with an empty name are not supported yet, except for the root
263 # namespace. For the root namespace, both arguments are empty.
267 # * `id`: `model_id` of the inner namespace. May be empty.
268 # * `full_name`: qualified name of the inner namespace. Use an empty name
269 # for the root namespace.
270 fun declare_namespace
(id
: String, full_name
: String) do end
272 # Declare an inner class.
274 # Note: Althought Doxygen indicates that both arguments are optional,
275 # declarations with an empty ID are not supported yet.
279 # * `id`: `model_id` of the inner class.
280 # * `name`: short name of the inner class.
281 # * `prot`: visibility (proctection).
282 fun declare_class
(id
: String, name
: String, prot
: String) do end
284 # Declare a base compound (usually, a base class).
288 # * `id`: `model_id` of the base compound. May be empty.
289 # * `full_name`: qualified name of the base compound. May be empty.
290 # * `prot`: visibility (proctection) of the relationship.
291 # * `virt`: level of virtuality of the relationship.
292 fun declare_super
(id
: String, full_name
: String, prot
: String,
296 # An unrecognized compound.
298 # Used to simplify the handling of ignored entities.
299 class UnknownCompound
302 redef fun put_in_graph
do end
303 redef fun put_edges
do end
308 # Corresponds to a group in Nit.
312 # The inner namespaces.
314 # Left empty for the root namespace.
315 var inner_namespaces
: SimpleCollection[NamespaceRef] = new Array[NamespaceRef]
319 self.labels
.add
("MGroup")
322 redef fun declare_namespace
(id
: String, full_name
: String) do
323 inner_namespaces
.add
new NamespaceRef(id
, full_name
)
326 redef fun declare_class
(id
: String, full_name
: String, prot
: String) do
327 graph
.class_to_ns
[id
] = self
330 redef fun put_in_graph
do
332 var full_name
= self["full_name"]
333 if full_name
isa String then graph
.namespaces
[full_name
] = self
336 redef fun put_edges
do
338 graph
.add_edge
(self, "PROJECT", graph
.project
)
339 if self["name"] == self["full_name"] and self["full_name"] != "" then
340 # The root namespace does not know its children.
341 var root
= graph
.by_id
[""]
342 graph
.add_edge
(self, "PARENT", root
)
343 graph
.add_edge
(root
, "NESTS", self)
345 for ns
in inner_namespaces
do
346 var node
= ns
.seek_in
(graph
)
347 graph
.add_edge
(node
, "PARENT", self)
348 graph
.add_edge
(self, "NESTS", node
)
353 # A reference to a namespace.
355 # The `model_id` of the target.
357 # Empty when unknown or for the root namespace.
360 # The `full_name` of the target.
362 # Empty only for the root namespace.
363 var full_name
: String
365 # Look for the targeted namespace in the specified graph.
366 fun seek_in
(graph
: ProjectGraph): Namespace do
367 var ns_compound
: Namespace
369 if model_id
.is_empty
and not full_name
.is_empty
then
370 # ID unspecified. => We have to look by name
371 assert graph
.namespaces
.has_key
(full_name
) else
372 sys
.stderr
.write
"Namespace `{full_name}` not found."
374 ns_compound
= graph
.namespaces
[full_name
]
376 ns_compound
= graph
.by_id
[model_id
].as(Namespace)
382 # The root namespace of a `ProjectGraph`.
384 # This the only entity in the graph whose `model_id` is really `""`.
385 # Added automatically at the initialization of a `ProjectGraph`.
391 self["full_name"] = ""
392 self["name"] = graph
.project_name
395 redef fun declare_namespace
(id
: String, name
: String) do end