nitweb: use config file
[nit.git] / src / web / api_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 # Render various graph from a model.
16 module api_graph
17
18 import web_base
19 import dot
20 import uml
21
22 # Group all api handlers in one router.
23 class APIGraphRouter
24 super APIRouter
25
26 init do
27 use("/inheritance/:id", new APIInheritanceGraph(config))
28 end
29 end
30
31 # Render a hierarchy graph for `mentity` if any.
32 class APIInheritanceGraph
33 super APIHandler
34
35 redef fun get(req, res) do
36 var pdepth = req.int_arg("pdepth")
37 var cdepth = req.int_arg("cdepth")
38 var mentity = mentity_from_uri(req, res)
39 if mentity == null then
40 res.error 404
41 return
42 end
43 var g = new InheritanceGraph(mentity, view)
44 res.send g.draw(pdepth, cdepth).to_svg
45 end
46 end
47
48 # Graph for mentity hierarchies
49 #
50 # Recursively build parents and children list from a `center`.
51 class InheritanceGraph
52 super ModelVisitor
53
54 # MEntity at the center of this graph
55 var center: MEntity
56
57 # ModelView used to filter graph
58 var view: ModelView
59
60 # Graph generated
61 var graph: DotGraph is lazy do
62 var graph = new DotGraph("package_diagram", "digraph")
63
64 graph["compound"] = "true"
65 graph["rankdir"] = "BT"
66 graph["ranksep"] = 0.3
67 graph["nodesep"] = 0.3
68
69 graph.nodes_attrs["margin"] = 0.1
70 graph.nodes_attrs["width"] = 0
71 graph.nodes_attrs["height"] = 0
72 graph.nodes_attrs["fontsize"] = 10
73 graph.nodes_attrs["fontname"] = "helvetica"
74
75 graph.edges_attrs["dir"] = "none"
76 graph.edges_attrs["color"] = "gray"
77
78 return graph
79 end
80
81 # Build the graph
82 fun draw(parents_depth, children_depth: nullable Int): DotGraph do
83 draw_node center
84 draw_parents(center, parents_depth)
85 draw_children(center, children_depth)
86 return graph
87 end
88
89 private var nodes = new HashMap[MEntity, DotElement]
90 private var done_parents = new HashSet[MEntity]
91 private var done_children = new HashSet[MEntity]
92
93 # Recursively draw parents of mentity
94 fun draw_parents(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
95 if done_parents.has(mentity) then return
96 done_parents.add mentity
97 current_depth = current_depth or else 0
98 if max_depth != null and current_depth >= max_depth then
99 from_dotdotdot(mentity)
100 return
101 end
102 var parents = mentity.collect_parents(view)
103 if parents.length > 10 then
104 from_dotdotdot(mentity)
105 return
106 end
107 for parent in parents do
108 if parent isa MModule then
109 var mgroup = parent.mgroup
110 if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
111 end
112 if parent isa MGroup then
113 if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
114 end
115 draw_edge(mentity, parent)
116 end
117 for parent in parents do
118 if parent isa MModule then
119 var mgroup = parent.mgroup
120 if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
121 end
122 if parent isa MGroup then
123 if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
124 end
125 draw_parents(parent, max_depth, current_depth + 1)
126 end
127 end
128
129 # Recursively draw children of mentity
130 fun draw_children(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
131 if done_children.has(mentity) then return
132 done_children.add mentity
133 current_depth = current_depth or else 0
134 if max_depth != null and current_depth >= max_depth then
135 to_dotdotdot(mentity)
136 return
137 end
138 var children = mentity.collect_children(view)
139 if children.length > 10 then
140 to_dotdotdot(mentity)
141 return
142 end
143 for child in children do
144 if child isa MGroup then
145 if child.mpackage.mgroups.first == child then child = child.mpackage
146 end
147 draw_edge(child, mentity)
148 end
149 for child in children do
150 if child isa MGroup then
151 if child.mpackage.mgroups.first == child then child = child.mpackage
152 end
153 draw_children(child, max_depth, current_depth + 1)
154 end
155 end
156
157 # Draw a node from a `mentity`
158 fun draw_node(mentity: MEntity): DotElement do
159 if nodes.has_key(mentity) then return nodes[mentity]
160 var node: DotElement = mentity.to_dot_node
161 if mentity == center then node = highlight(node)
162 nodes[mentity] = node
163 graph.add node
164 return node
165 end
166
167 private var edges = new HashMap2[MEntity, MEntity, DotEdge]
168
169 # Draw a edges between two mentities
170 fun draw_edge(from, to: MEntity): DotEdge do
171 if edges.has(from, to) then return edges[from, to].as(not null)
172 if edges.has(to, from) then return edges[to, from].as(not null)
173 var nfrom = draw_node(from)
174 var nto = draw_node(to)
175 var edge = new DotEdge(nfrom, nto)
176 edges[from, to] = edge
177 graph.add edge
178 return edge
179 end
180
181 private var to_dots = new HashMap[MEntity, DotElement]
182
183 # Create a link from `mentity` to a `...` node
184 fun to_dotdotdot(mentity: MEntity): DotEdge do
185 var nto = draw_node(mentity)
186 var dots = to_dots.get_or_null(mentity)
187 if dots == null then
188 dots = dotdotdot("{nto.id}...")
189 to_dots[mentity] = dots
190 end
191 graph.add dots
192 var edge = new DotEdge(dots, nto)
193 graph.add edge
194 return edge
195 end
196
197 private var from_dots = new HashMap[MEntity, DotElement]
198
199 # Create a link from a `...` node to a `mentity`
200 fun from_dotdotdot(mentity: MEntity): DotEdge do
201 var nfrom = draw_node(mentity)
202 var dots = to_dots.get_or_null(mentity)
203 if dots == null then
204 dots = dotdotdot("...{nfrom.id}")
205 from_dots[mentity] = dots
206 end
207 graph.add dots
208 var edge = new DotEdge(dots, nfrom)
209 graph.add edge
210 return edge
211 end
212
213 # Change the border color of the node
214 fun highlight(dot: DotElement): DotElement do
215 dot["color"] = "#1E9431"
216 return dot
217 end
218
219 # Generate a `...` node
220 fun dotdotdot(id: String): DotNode do
221 var node = new DotNode(id)
222 node["label"] = "..."
223 node["shape"] = "none"
224 return node
225 end
226 end
227
228 redef class MEntity
229 private fun to_dot_node: DotNode do
230 var node = new DotNode(full_name)
231 node["URL"] = web_url
232 node["label"] = name
233 return node
234 end
235 end
236
237 redef class MPackage
238 redef fun to_dot_node do
239 var node = super
240 node["shape"] = "tab"
241 return node
242 end
243 end
244
245 redef class MGroup
246 redef fun to_dot_node do
247 var node = super
248 node["shape"] = "folder"
249 return node
250 end
251 end
252
253 redef class MModule
254 redef fun to_dot_node do
255 var node = super
256 node["shape"] = "note"
257 return node
258 end
259 end
260
261 redef class MClass
262 redef fun to_dot_node do
263 var node = super
264 node["shape"] = "box"
265 return node
266 end
267 end