neo_doxygen: Add a class to manage brief descriptions.
[nit.git] / contrib / neo_doxygen / src / model / graph.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Graphs and basic entities.
16 module model::graph
17
18 import neo4j
19 import more_collections
20 import location
21 import descriptions
22
23 # A Neo4j graph.
24 class NeoGraph
25 # All the nodes in the graph.
26 var all_nodes: SimpleCollection[NeoNode] = new Array[NeoNode]
27
28 # All the edges in the graph.
29 var all_edges: SimpleCollection[NeoEdge] = new Array[NeoEdge]
30
31 # Add a relationship between two nodes.
32 #
33 # Parameters are the same than for the constructor of `NeoEdge`.
34 fun add_edge(from: NeoNode, rel_type: String, to: NeoNode) do
35 all_edges.add(new NeoEdge(from, rel_type, to))
36 end
37 end
38
39 # A project’s graph.
40 #
41 # Here is the usual steps to build a project graph:
42 #
43 # <ul>
44 # <li>Instantiate `ProjectGraph` by giving the name that will label the project.</li>
45 # <li>For each compound:
46 # <ul>
47 # <li>Instantiate the compound.</li>
48 # <li>Provide all the related data.</li>
49 # <li>Call the `put_in_graph` method of the compound.</li>
50 # </ul></li>
51 # <li>Call the `add_global_modules` method of the project’s graph (defined in
52 # the `module_compound` module). This permits to take global classes into
53 # account correctly.</li>
54 # <li>Call the `put_edges` method of the project’s graph.</li>
55 # </ul>
56 class ProjectGraph
57 super NeoGraph
58
59 # The project’s name.
60 var project_name: String
61
62 # The node reperesenting the project.
63 #
64 # Once the project’s graph is initialized, this node must not be edited.
65 var project = new NeoNode
66
67 # Entities by `model_id`.
68 var by_id: Map[String, Entity] = new HashMap[String, Entity]
69
70 # Namespaces by `full_name`.
71 var namespaces: Map[String, Namespace] = new HashMap[String, Namespace]
72
73 # For each `ClassCompound` in the graph, the mapping between its `model_id` and its namespace.
74 #
75 # Defaults to the root namespace. An entry is added each time
76 # `Namespace.declare_class` is called.
77 #
78 # Note: In the graph, there is no direct link between a namespace and a
79 # class. It is the role of a module (created internally by a `FileCompound`)
80 # to link a class with its namespace. So, this collection is used by modules
81 # to know which class in a file belong to their related namespace. It is
82 # also used by `FileCompound` to detect classes in the root namespace.
83 var class_to_ns: Map[String, Namespace] is noinit
84
85 # Initialize a new project graph using the specified project name.
86 #
87 # The specified name will label all nodes of the project’s graph.
88 init do
89 project.labels.add(project_name)
90 project.labels.add("MEntity")
91 project.labels.add("MProject")
92 project["name"] = project_name
93 all_nodes.add(project)
94
95 var root = new RootNamespace(self)
96 root.put_in_graph
97 by_id[""] = root
98 class_to_ns = new DefaultMap[String, Namespace](root)
99 end
100
101 # Request to all nodes in the graph to add their related edges.
102 #
103 # Note: For the rare cases where a node need to wait the `put_edges` to add
104 # an implicit node, this method makes sure to call the `put_edges` method
105 # of the newly added nodes only after processing all the nodes that was
106 # already there.
107 fun put_edges do
108 all_edges.clear
109 add_edge(project, "ROOT", by_id[""])
110 for n in all_nodes do
111 if n isa Entity then
112 n.put_edges
113 end
114 end
115 end
116 end
117
118 # A model’s entity.
119 #
120 # In practice, this is the base class of every node in a `ProjectGraph`.
121 abstract class Entity
122 super NeoNode
123
124 # Graph that will embed the entity.
125 var graph: ProjectGraph
126
127 # ID of the entity in the model.
128 #
129 # Is empty for entities without an ID.
130 var model_id: String = "" is writable
131
132 # The full (qualified) name, as presented by the original model.
133 #
134 # Fully independant of `name`. By default, equals to `""` for the root
135 # namespace.
136 var full_name: nullable String = null is writable
137
138 # Associated documentation.
139 var doc = new Documentation is writable
140
141 init do
142 self.labels.add(graph.project_name)
143 self.labels.add("MEntity")
144 end
145
146 # The short (unqualified) name.
147 fun name=(name: String) do
148 self["name"] = name
149 end
150
151 # The short (unqualified) name.
152 fun name: String do
153 var name = self["name"]
154 assert name isa String
155 return name
156 end
157
158 # Include the documentation of `self` in the graph.
159 protected fun set_mdoc do
160 self["mdoc"] = doc
161 end
162
163 # The namespace separator of Nit/C++.
164 #
165 # Used to join two or more names when we need to work around some
166 # limitations of the Nit model.
167 fun ns_separator: String do return "::"
168
169 # Set the location of the entity in the source code.
170 fun location=(location: nullable Location) do
171 self["location"] = location
172 end
173
174 # Get the location of the entity in the source code.
175 fun location: nullable Location do
176 return self["location"].as(nullable Location)
177 end
178
179 # Put the entity in the graph.
180 #
181 # Called by the loader when it has finished to read the entity.
182 fun put_in_graph do
183 if not doc.is_empty then
184 set_mdoc
185 end
186 graph.all_nodes.add(self)
187 if model_id != "" then graph.by_id[model_id] = self
188 end
189
190 # Put the related edges in the graph.
191 #
192 # This method is called on each node by `ProjectGraph.put_edges`.
193 #
194 # Note: Even at this step, the entity may modify its own attributes and
195 # inner entities’ ones because some values are only known once the entity
196 # know its relationships with the rest of the graph.
197 fun put_edges do end
198 end
199
200 # An entity whose the location is mandatory.
201 abstract class CodeBlock
202 super Entity
203
204 init do
205 self["location"] = new Location
206 end
207
208 redef fun location=(location: nullable Location) do
209 if location == null then
210 super(new Location)
211 else
212 super
213 end
214 end
215 end
216
217 # A compound.
218 #
219 # Usually corresponds to a `<compounddef>` element in of the XML output of
220 # Doxygen.
221 abstract class Compound
222 super Entity
223
224 # Set the declared visibility (the proctection) of the compound.
225 fun visibility=(visibility: String) do
226 self["visibility"] = visibility
227 end
228
229 # Set the specific kind of the compound.
230 fun kind=(kind: String) do
231 self["kind"] = kind
232 end
233
234 # Declare an inner namespace.
235 #
236 # Note: Althought Doxygen indicates that the name is optional,
237 # declarations with an empty name are not supported yet, except for the root
238 # namespace. For the root namespace, both arguments are empty.
239 #
240 # Parameters:
241 #
242 # * `id`: `model_id` of the inner namespace. May be empty.
243 # * `full_name`: qualified name of the inner namespace. Use an empty name
244 # for the root namespace.
245 fun declare_namespace(id: String, full_name: String) do end
246
247 # Declare an inner class.
248 #
249 # Note: Althought Doxygen indicates that both arguments are optional,
250 # declarations with an empty ID are not supported yet.
251 #
252 # Parameters:
253 #
254 # * `id`: `model_id` of the inner class.
255 # * `name`: short name of the inner class.
256 # * `prot`: visibility (proctection).
257 fun declare_class(id: String, name: String, prot: String) do end
258
259 # Declare a base compound (usually, a base class).
260 #
261 # Parameters:
262 #
263 # * `id`: `model_id` of the base compound. May be empty.
264 # * `full_name`: qualified name of the base compound. May be empty.
265 # * `prot`: visibility (proctection) of the relationship.
266 # * `virt`: level of virtuality of the relationship.
267 fun declare_super(id: String, full_name: String, prot: String,
268 virt: String) do end
269 end
270
271 # An unrecognized compound.
272 #
273 # Used to simplify the handling of ignored entities.
274 class UnknownCompound
275 super Compound
276
277 redef fun put_in_graph do end
278 redef fun put_edges do end
279 end
280
281 # A namespace.
282 #
283 # Corresponds to a group in Nit.
284 class Namespace
285 super Compound
286
287 # The inner namespaces.
288 var inner_namespaces: SimpleCollection[NamespaceRef] = new Array[NamespaceRef]
289
290 init do
291 super
292 self.labels.add("MGroup")
293 end
294
295 redef fun declare_namespace(id: String, full_name: String) do
296 inner_namespaces.add new NamespaceRef(id, full_name)
297 end
298
299 redef fun declare_class(id, name, prot) do
300 assert not id.is_empty else
301 sys.stderr.write "Inner class declarations without ID are not yet supported.\n"
302 end
303 graph.class_to_ns[id] = self
304 end
305
306 redef fun put_in_graph do
307 super
308 var full_name = self.full_name
309 if full_name isa String then graph.namespaces[full_name] = self
310 end
311
312 redef fun put_edges do
313 super
314 graph.add_edge(self, "PROJECT", graph.project)
315 for ns in inner_namespaces do
316 var node = ns.seek_in(graph)
317 graph.add_edge(node, "PARENT", self)
318 graph.add_edge(self, "NESTS", node)
319 end
320 end
321 end
322
323 # A reference to a namespace.
324 class NamespaceRef
325 # The `model_id` of the target.
326 #
327 # Empty when unknown or for the root namespace.
328 var model_id: String
329
330 # The `full_name` of the target.
331 #
332 # Empty only for the root namespace.
333 var full_name: String
334
335 # Look for the targeted namespace in the specified graph.
336 fun seek_in(graph: ProjectGraph): Namespace do
337 var ns_compound: Namespace
338
339 if model_id.is_empty and not full_name.is_empty then
340 # ID unspecified. => We have to look by name
341 assert graph.namespaces.has_key(full_name) else
342 sys.stderr.write "Namespace `{full_name}` not found."
343 end
344 ns_compound = graph.namespaces[full_name]
345 else
346 ns_compound = graph.by_id[model_id].as(Namespace)
347 end
348 return ns_compound
349 end
350 end
351
352 # The root namespace of a `ProjectGraph`.
353 #
354 # This the only entity in the graph whose `model_id` is really `""`.
355 # Added automatically at the initialization of a `ProjectGraph`.
356 class RootNamespace
357 super Namespace
358
359 init do
360 super
361 full_name = ""
362 name = graph.project_name
363 end
364 end