all: add `nitish` tag for code-blocks skipped by nitunit
[nit.git] / src / model_utils.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Model exploration and traversing facilities
18 module model_utils
19
20 import model
21
22 redef class MConcern
23
24 # Boost a MConcern rank
25 # see: `MConcernRankSorter`
26 # Use a positive booster to push down a result in the list
27 # A negative booster can be used to push up the result
28 var booster_rank: Int = 0 is writable
29
30 # Concern ranking used for ordering
31 # see: `MConcernRankSorter`
32 # Rank can be positive or negative
33 fun concern_rank: Int is abstract
34 end
35
36 redef class MProject
37 redef fun concern_rank is cached do
38 var max = 0
39 for mgroup in mgroups do
40 var mmax = mgroup.concern_rank
41 if mmax > max then max = mmax
42 end
43 return max + 1
44 end
45 end
46
47 redef class MGroup
48 fun in_nesting_intro_mclasses(min_visibility: MVisibility): Set[MClass] do
49 var res = new HashSet[MClass]
50 var lst = in_nesting.direct_smallers
51 for mmodule in mmodules do res.add_all mmodule.filter_intro_mclasses(min_visibility)
52 for mgrp in lst do res.add_all mgrp.in_nesting_intro_mclasses(min_visibility)
53 return res
54 end
55
56 fun in_nesting_redef_mclasses(min_visibility: MVisibility): Set[MClass] do
57 var res = new HashSet[MClass]
58 var lst = in_nesting.direct_smallers
59 for mmodule in mmodules do res.add_all mmodule.filter_redef_mclasses(min_visibility)
60 for mgrp in lst do res.add_all mgrp.in_nesting_redef_mclasses(min_visibility)
61 return res
62 end
63
64 fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
65 var res = new HashSet[MClassDef]
66 var lst = in_nesting.direct_smallers
67 for mmodule in mmodules do res.add_all mmodule.intro_mclassdefs(min_visibility)
68 for mgrp in lst do res.add_all mgrp.in_nesting_intro_mclassdefs(min_visibility)
69 return res
70 end
71
72 fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
73 var res = new HashSet[MClassDef]
74 var lst = in_nesting.direct_smallers
75 for mmodule in mmodules do res.add_all mmodule.redef_mclassdefs(min_visibility)
76 for mgrp in lst do res.add_all mgrp.in_nesting_redef_mclassdefs(min_visibility)
77 return res
78 end
79
80 # Collect nested modules
81 fun collect_mmodules: Set[MModule] do
82 var res = new HashSet[MModule]
83 res.add_all mmodules
84 for mgroup in in_nesting.direct_smallers do
85 res.add_all mgroup.collect_mmodules
86 end
87 return res
88 end
89
90 redef fun concern_rank is cached do
91 var max = 0
92 for mmodule in collect_mmodules do
93 var mmax = mmodule.concern_rank
94 if mmax > max then max = mmax
95 end
96 return max + 1
97 end
98 end
99
100 redef class MModule
101
102 # The list of intro mclassdef in the module.
103 # with visibility >= to min_visibility
104 fun intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
105 var res = new HashSet[MClassDef]
106 for mclassdef in mclassdefs do
107 if not mclassdef.is_intro then continue
108 if mclassdef.mclass.visibility < min_visibility then continue
109 res.add mclassdef
110 end
111 return res
112 end
113
114 # The list of redef mclassdef in the module.
115 # with visibility >= to min_visibility
116 fun redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
117 var res = new HashSet[MClassDef]
118 for mclassdef in mclassdefs do
119 if mclassdef.is_intro then continue
120 if mclassdef.mclass.visibility < min_visibility then continue
121 res.add mclassdef
122 end
123 return res
124 end
125
126 # The list of intro mclass in the module.
127 # with visibility >= to min_visibility
128 fun filter_intro_mclasses(min_visibility: MVisibility): Set[MClass] do
129 var res = new HashSet[MClass]
130 for mclass in intro_mclasses do
131 if mclass.visibility < min_visibility then continue
132 res.add mclass
133 end
134 return res
135 end
136
137 # Get the list of mclasses refined in 'self'.
138 fun redef_mclasses: Set[MClass] do
139 var mclasses = new HashSet[MClass]
140 for c in mclassdefs do
141 if not c.is_intro then mclasses.add(c.mclass)
142 end
143 return mclasses
144 end
145
146 # Get the list of mclasses refined in 'self'.
147 fun filter_redef_mclasses(min_visibility: MVisibility): Set[MClass] do
148 var mclasses = new HashSet[MClass]
149 for c in mclassdefs do
150 if c.mclass.visibility < min_visibility then continue
151 if not c.is_intro then mclasses.add(c.mclass)
152 end
153 return mclasses
154 end
155
156 # Get the list of all mclasses imported by 'self'.
157 fun imported_mclasses: Set[MClass] do
158 var mclasses = new HashSet[MClass]
159 for m in in_importation.greaters do
160 if m == self then continue
161 for c in m.mclassdefs do mclasses.add(c.mclass)
162 end
163 return mclasses
164 end
165
166 fun in_nesting_intro_mclasses(min_visibility: MVisibility): Set[MClass] do
167 var res = new HashSet[MClass]
168 for mmodule in in_nesting.greaters do
169 for mclass in mmodule.filter_intro_mclasses(min_visibility) do
170 if mclass.visibility < min_visibility then continue
171 res.add mclass
172 end
173 end
174 return res
175 end
176
177 fun in_nesting_redef_mclasses(min_visibility: MVisibility): Set[MClass] do
178 var res = new HashSet[MClass]
179 for mmodule in self.in_nesting.greaters do
180 for mclass in mmodule.filter_redef_mclasses(min_visibility) do
181 if mclass.visibility < min_visibility then continue
182 res.add mclass
183 end
184 end
185 return res
186 end
187
188 fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
189 var res = new HashSet[MClassDef]
190 for mmodule in in_nesting.greaters do
191 res.add_all mmodule.intro_mclassdefs(min_visibility)
192 end
193 return res
194 end
195
196 fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
197 var res = new HashSet[MClassDef]
198 for mmodule in self.in_nesting.greaters do
199 res.add_all mmodule.redef_mclassdefs(min_visibility)
200 end
201 return res
202 end
203
204 redef fun concern_rank is cached do
205 var max = 0
206 for p in in_importation.direct_greaters do
207 var pmax = p.concern_rank
208 if pmax > max then max = pmax
209 end
210 return max + 1
211 end
212 end
213
214 redef class MClass
215
216 # Get direct parents of 'self'.
217 fun parents: Set[MClass] do
218 var ret = new HashSet[MClass]
219 for mclassdef in mclassdefs do
220 for mclasstype in mclassdef.supertypes do
221 ret.add(mclasstype.mclass)
222 end
223 end
224 return ret
225 end
226
227 # Get all ancestors of 'self'.
228 fun ancestors: Set[MClass] do
229 var lst = new HashSet[MClass]
230 for mclassdef in self.mclassdefs do
231 for super_mclassdef in mclassdef.in_hierarchy.greaters do
232 if super_mclassdef == mclassdef then continue # skip self
233 lst.add(super_mclassdef.mclass)
234 end
235 end
236 return lst
237 end
238
239 # Get direct children of 'self'.
240 fun children: Set[MClass] do
241 var lst = new HashSet[MClass]
242 for mclassdef in self.mclassdefs do
243 for sub_mclassdef in mclassdef.in_hierarchy.direct_smallers do
244 if sub_mclassdef == mclassdef then continue # skip self
245 lst.add(sub_mclassdef.mclass)
246 end
247 end
248 return lst
249 end
250
251 # Get all children of 'self'.
252 fun descendants: Set[MClass] do
253 var lst = new HashSet[MClass]
254 for mclassdef in self.mclassdefs do
255 for sub_mclassdef in mclassdef.in_hierarchy.smallers do
256 if sub_mclassdef == mclassdef then continue # skip self
257 lst.add(sub_mclassdef.mclass)
258 end
259 end
260 return lst
261 end
262
263 # Get the list of constructors available for 'self'.
264 fun constructors: Set[MMethod] do
265 var res = new HashSet[MMethod]
266 for mclassdef in mclassdefs do
267 for mpropdef in mclassdef.mpropdefs do
268 if mpropdef isa MMethodDef then
269 if mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
270 end
271 end
272 end
273 return res
274 end
275
276 # Get the list of methods introduced in 'self'.
277 fun intro_methods: Set[MMethod] do
278 var res = new HashSet[MMethod]
279 for mclassdef in mclassdefs do
280 for mpropdef in mclassdef.mpropdefs do
281 if mpropdef isa MMethodDef then
282 if mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
283 end
284 end
285 end
286 return res
287 end
288
289 # the set of properties introduced in 'self'.
290 fun intro_mproperties(min_visibility: MVisibility): Set[MProperty] do
291 var set = new HashSet[MProperty]
292 for mclassdef in mclassdefs do
293 for mprop in mclassdef.intro_mproperties do
294 if mprop.visibility < min_visibility then continue
295 set.add(mprop)
296 end
297 end
298 return set
299 end
300
301 fun intro_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
302 var set = new HashSet[MPropDef]
303 for mclassdef in mclassdefs do
304 for mpropdef in mclassdef.mpropdefs do
305 if not mpropdef.is_intro then continue
306 if mpropdef.mproperty.visibility < min_visibility then continue
307 set.add(mpropdef)
308 end
309 end
310 return set
311 end
312
313 # the set of locally refined properties in 'self'.
314 fun redef_mproperties(min_visibility: MVisibility): Set[MProperty] do
315 var set = new HashSet[MProperty]
316 for mclassdef in mclassdefs do
317 for mpropdef in mclassdef.mpropdefs do
318 if mpropdef.mproperty.visibility < min_visibility then continue
319 if mpropdef.mproperty.intro_mclassdef.mclass != self then set.add(mpropdef.mproperty)
320 end
321 end
322 return set
323 end
324
325 fun redef_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
326 var set = new HashSet[MPropDef]
327 for mclassdef in mclassdefs do
328 for mpropdef in mclassdef.mpropdefs do
329 if mpropdef.is_intro then continue
330 if mpropdef.mproperty.visibility < min_visibility then continue
331 set.add(mpropdef)
332 end
333 end
334 return set
335 end
336
337 # the set of methods inherited by 'self'.
338 fun inherited_mproperties(mainmodule: MModule, min_visibility: MVisibility): Set[MProperty] do
339 var set = new HashSet[MProperty]
340 for parent in in_hierarchy(mainmodule).direct_greaters do
341 set.add_all(parent.intro_mproperties(min_visibility))
342 set.add_all(parent.inherited_mproperties(mainmodule, min_visibility))
343 end
344 return set
345 end
346
347 # the set of introduced and redefined mproperties
348 fun local_mproperties(min_visibility: MVisibility): Set[MProperty] do
349 var set = new HashSet[MProperty]
350 set.add_all(intro_mproperties(min_visibility))
351 set.add_all(redef_mproperties(min_visibility))
352 return set
353 end
354
355 # the set of all accessible mproperties for this class
356 fun all_mproperties(mainmodule: MModule, min_visibility: MVisibility): Set[MProperty] do
357 var set = new HashSet[MProperty]
358 set.add_all(local_mproperties(min_visibility))
359 set.add_all(inherited_mproperties(mainmodule, min_visibility))
360 return set
361 end
362
363 # the set of all accessible mattributes for this class
364 fun all_mattributes(mainmodule: MModule, min_visibility: MVisibility): Set[MAttribute] do
365 var set = new HashSet[MAttribute]
366 for mprop in all_mproperties(mainmodule, min_visibility) do
367 if mprop isa MAttribute then set.add(mprop)
368 end
369 return set
370 end
371
372 # Get the list of locally refined methods in 'self'.
373 fun redef_methods: Set[MMethod] do
374 var res = new HashSet[MMethod]
375 for mclassdef in mclassdefs do
376 for mpropdef in mclassdef.mpropdefs do
377 if mpropdef isa MMethodDef then
378 if not mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
379 end
380 end
381 end
382 return res
383 end
384
385 fun inherited_methods: Set[MMethod] do
386 var res = new HashSet[MMethod]
387 for s in ancestors do
388 for m in s.intro_methods do
389 if not self.intro_methods.has(m) and not self.redef_methods.has(m) then res.add(m)
390 end
391 end
392 return res
393 end
394
395 # Get the list of all virtual types available in 'self'.
396 fun virtual_types: Set[MVirtualTypeProp] do
397 var res = new HashSet[MVirtualTypeProp]
398 for mclassdef in mclassdefs do
399 for mpropdef in mclassdef.mpropdefs do
400 if mpropdef isa MVirtualTypeDef then
401 res.add(mpropdef.mproperty)
402 end
403 end
404 end
405 for ancestor in ancestors do
406 for mclassdef in ancestor.mclassdefs do
407 for mpropdef in mclassdef.mpropdefs do
408 if mpropdef isa MVirtualTypeDef then
409 res.add(mpropdef.mproperty)
410 end
411 end
412 end
413 end
414 return res
415 end
416
417 # Get the list of all parameter types in 'self'.
418 fun parameter_types: Map[String, MType] do
419 var res = new HashMap[String, MType]
420 for p in mparameters do
421 res[p.name] = p
422 end
423 return res
424 end
425
426 fun is_class: Bool do
427 return self.kind == concrete_kind or self.kind == abstract_kind
428 end
429
430 fun is_interface: Bool do
431 return self.kind == interface_kind
432 end
433
434 fun is_enum: Bool do
435 return self.kind == enum_kind
436 end
437
438 fun is_abstract: Bool do
439 return self.kind == abstract_kind
440 end
441 end
442
443 redef class MAttribute
444 # Is this attribute nullable for sure?
445 #
446 # This mean that its introduction is declarred with a nullable static type
447 # since attributes are invariant this will work on most cases
448 # attributes with static type anchored with a virtual type are not "nullable for-sure"
449 # because this type can be redefined in subclasses
450 fun is_nullable: Bool do return intro.static_mtype isa MNullableType
451 end
452
453 redef class MClassDef
454 # modifiers are keywords like redef, private etc.
455 fun modifiers: Array[String] do
456 var res = new Array[String]
457 if not is_intro then
458 res.add "redef"
459 else
460 res.add mclass.visibility.to_s
461 end
462 res.add mclass.kind.to_s
463 return res
464 end
465
466 fun collect_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
467 var res = new HashSet[MPropDef]
468 for mpropdef in mpropdefs do
469 if mpropdef.mproperty.visibility < min_visibility then continue
470 res.add mpropdef
471 end
472 return res
473 end
474
475 fun collect_intro_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
476 var res = new HashSet[MPropDef]
477 for mpropdef in mpropdefs do
478 if not mpropdef.is_intro then continue
479 if mpropdef.mproperty.visibility < min_visibility then continue
480 res.add mpropdef
481 end
482 return res
483 end
484
485 fun collect_redef_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
486 var res = new HashSet[MPropDef]
487 for mpropdef in mpropdefs do
488 if mpropdef.is_intro then continue
489 if mpropdef.mproperty.visibility < min_visibility then continue
490 res.add mpropdef
491 end
492 return res
493 end
494 end
495
496 redef class MPropDef
497 # modifiers are keywords like redef, private etc.
498 fun modifiers: Array[String] do
499 var res = new Array[String]
500 if not is_intro then
501 res.add "redef"
502 else
503 res.add mproperty.visibility.to_s
504 end
505 var mprop = self
506 if mprop isa MVirtualTypeDef then
507 res.add "type"
508 else if mprop isa MMethodDef then
509 if mprop.is_abstract then
510 res.add "abstract"
511 else if mprop.is_intern then
512 res.add "intern"
513 end
514 if mprop.mproperty.is_init then
515 res.add "init"
516 else
517 res.add "fun"
518 end
519 end
520 return res
521 end
522 end
523
524 # Sorters
525
526 # Sort mentities by their name
527 class MEntityNameSorter
528 super Comparator
529 redef type COMPARED: MEntity
530 redef fun compare(a, b) do return a.name <=> b.name
531 end
532
533 # Sort MConcerns based on the module importation hierarchy ranking
534 # see also: `MConcern::concern_rank` and `MConcern::booster_rank`
535 #
536 # Comparison is made with the formula:
537 #
538 # ~~~nitish
539 # a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
540 # ~~~
541 #
542 # If both `a` and `b` have the same ranking,
543 # ordering is based on lexicographic comparison of `a.name` and `b.name`
544 class MConcernRankSorter
545 super Comparator
546 redef type COMPARED: MConcern
547
548 redef fun compare(a, b) do
549 if a.concern_rank == b.concern_rank then
550 return a.name <=> b.name
551 end
552 return a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_rank
553 end
554 end