nitweb: use model index
[nit.git] / src / web / api_model.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 module api_model
16
17 import web_base
18 import highlight
19 import uml
20 import model::model_index
21
22 redef class APIRouter
23 redef init do
24 super
25 use("/list", new APIList(config))
26 use("/search", new APISearch(config))
27 use("/random", new APIRandom(config))
28 use("/entity/:id", new APIEntity(config))
29 use("/code/:id", new APIEntityCode(config))
30 use("/uml/:id", new APIEntityUML(config))
31 use("/linearization/:id", new APIEntityLinearization(config))
32 use("/defs/:id", new APIEntityDefs(config))
33 use("/inheritance/:id", new APIEntityInheritance(config))
34 end
35 end
36
37 # List all mentities.
38 #
39 # MEntities can be filtered on their kind using the `k` parameter.
40 # Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
41 #
42 # List size can be limited with the `n` parameter.
43 #
44 # Example: `GET /list?k=module?n=10`
45 class APIList
46 super APIHandler
47
48 # List mentities depending on the `k` kind parameter.
49 fun list_mentities(req: HttpRequest): Array[MEntity] do
50 var k = req.string_arg("k")
51 var mentities = new Array[MEntity]
52 if k == "package" then
53 for mentity in config.view.mpackages do mentities.add mentity
54 else if k == "group" then
55 for mentity in config.view.mgroups do mentities.add mentity
56 else if k == "module" then
57 for mentity in config.view.mmodules do mentities.add mentity
58 else if k == "class" then
59 for mentity in config.view.mclasses do mentities.add mentity
60 else if k == "classdef" then
61 for mentity in config.view.mclassdefs do mentities.add mentity
62 else if k == "property" then
63 for mentity in config.view.mproperties do mentities.add mentity
64 else if k == "propdef" then
65 for mentity in config.view.mpropdefs do mentities.add mentity
66 else
67 for mentity in config.view.mentities do mentities.add mentity
68 end
69 return mentities
70 end
71
72 # Limit mentities depending on the `n` parameter.
73 fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
74 var n = req.int_arg("n")
75 if n != null then
76 return mentities.sub(0, n)
77 end
78 return mentities
79 end
80
81 redef fun get(req, res) do
82 var mentities = list_mentities(req)
83 mentities = limit_mentities(req, mentities)
84 res.json new JsonArray.from(mentities)
85 end
86 end
87
88 # Search mentities from a query string.
89 #
90 # Example: `GET /search?q=Arr`
91 class APISearch
92 super APIList
93
94 redef fun get(req, res) do
95 var q = req.string_arg("q")
96 if q == null then
97 res.json new JsonArray
98 return
99 end
100 var n = req.int_arg("n")
101 res.json new JsonArray.from(config.view.find(q, n))
102 end
103 end
104
105 # Return a random list of MEntities.
106 #
107 # Example: `GET /random?n=10&k=module`
108 class APIRandom
109 super APIList
110
111 # Randomize mentities order.
112 fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
113 var res = mentities.to_a
114 res.shuffle
115 return res
116 end
117
118 redef fun get(req, res) do
119 var mentities = list_mentities(req)
120 mentities = limit_mentities(req, mentities)
121 mentities = randomize_mentities(req, mentities)
122 res.json new JsonArray.from(mentities)
123 end
124 end
125
126 # Return the JSON representation of a MEntity.
127 #
128 # Example: `GET /entity/core::Array`
129 class APIEntity
130 super APIHandler
131
132 redef fun get(req, res) do
133 var mentity = mentity_from_uri(req, res)
134 if mentity == null then return
135 res.json mentity.api_json(self)
136 end
137 end
138
139 # List ancestors, parents, child and descendants of MEntity
140 #
141 # Example: `GET /entity/core::Array/inheritance`
142 class APIEntityInheritance
143 super APIHandler
144
145 redef fun get(req, res) do
146 var mentity = mentity_from_uri(req, res)
147 if mentity == null then return
148 res.json mentity.hierarchy_poset(config.view)[mentity]
149 end
150 end
151
152 # Linearize super definitions of a MClassDef or a MPropDef if any.
153 #
154 # Example: `GET /entity/core::Array/linearization`
155 class APIEntityLinearization
156 super APIHandler
157
158 redef fun get(req, res) do
159 var mentity = mentity_from_uri(req, res)
160 if mentity == null then return
161 var lin = mentity.collect_linearization(config.mainmodule)
162 if lin == null then
163 res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
164 return
165 end
166 var mentities = new JsonArray
167 for e in lin do mentities.add e.full_json
168 res.json mentities
169 end
170 end
171
172 # List definitions of a MEntity.
173 #
174 # Example: `GET /defs/core::Array`
175 class APIEntityDefs
176 super APIHandler
177
178 redef fun get(req, res) do
179 var mentity = mentity_from_uri(req, res)
180 if mentity == null then return
181 var arr = new JsonArray
182 if mentity isa MModule then
183 for mclassdef in mentity.mclassdefs do arr.add mclassdef
184 else if mentity isa MClass then
185 for mclassdef in mentity.mclassdefs do arr.add mclassdef
186 else if mentity isa MClassDef then
187 for mpropdef in mentity.mpropdefs do arr.add mpropdef
188 else if mentity isa MProperty then
189 for mpropdef in mentity.mpropdefs do arr.add mpropdef
190 else
191 res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
192 return
193 end
194 res.json arr
195 end
196 end
197
198 abstract class SVGHandler
199 super APIHandler
200
201 # Render a `dot` string as a svg image.
202 fun render_dot(dot: Text): String do
203 var proc = new ProcessDuplex("dot", "-Tsvg")
204 var svg = proc.write_and_read(dot)
205 proc.close
206 proc.wait
207 return svg
208 end
209 end
210
211 # Return a UML representation of MEntity.
212 #
213 # Example: `GET /entity/core::Array/uml`
214 class APIEntityUML
215 super SVGHandler
216
217 redef fun get(req, res) do
218 var mentity = mentity_from_uri(req, res)
219 if mentity == null then return
220 var dot
221 if mentity isa MClassDef then mentity = mentity.mclass
222 if mentity isa MClass then
223 var uml = new UMLModel(config.view, config.mainmodule)
224 dot = uml.generate_class_uml.write_to_string
225 else if mentity isa MModule then
226 var uml = new UMLModel(config.view, mentity)
227 dot = uml.generate_package_uml.write_to_string
228 else
229 res.api_error(404, "No UML for mentity `{mentity.full_name}`")
230 return
231 end
232 res.send render_dot(dot)
233 end
234 end
235
236 # Return the source code of MEntity.
237 #
238 # Example: `GET /entity/core::Array/code`
239 class APIEntityCode
240 super APIHandler
241
242 redef fun get(req, res) do
243 var mentity = mentity_from_uri(req, res)
244 if mentity == null then return
245 var source = render_source(mentity)
246 if source == null then
247 res.api_error(404, "No code for mentity `{mentity.full_name}`")
248 return
249 end
250 res.send source
251 end
252
253 # Highlight `mentity` source code.
254 private fun render_source(mentity: MEntity): nullable HTMLTag do
255 var node = config.modelbuilder.mentity2node(mentity)
256 if node == null then return null
257 var hl = new HighlightVisitor
258 hl.enter_visit node
259 return hl.html
260 end
261 end