model_utils: creates a replacement for MModule::in_nesting.
[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
213 # Find all mmodules nested in `self` if `self` is the default module of a `MGroup`.
214 fun nested_mmodules: Array[MModule] do
215 var res = new Array[MModule]
216 var mgroup = mgroup
217 if mgroup == null or self != mgroup.default_mmodule then return res
218 for mmodule in mgroup.mmodules do
219 if mmodule == self then continue
220 res.add mmodule
221 end
222 for nested in mgroup.in_nesting.direct_smallers do
223 var default = nested.default_mmodule
224 if default == null then continue
225 res.add default
226 end
227 return res
228 end
229 end
230
231 redef class MClass
232
233 # Get direct parents of 'self'.
234 fun parents: Set[MClass] do
235 var ret = new HashSet[MClass]
236 for mclassdef in mclassdefs do
237 for mclasstype in mclassdef.supertypes do
238 ret.add(mclasstype.mclass)
239 end
240 end
241 return ret
242 end
243
244 # Get all ancestors of 'self'.
245 fun ancestors: Set[MClass] do
246 var lst = new HashSet[MClass]
247 for mclassdef in self.mclassdefs do
248 for super_mclassdef in mclassdef.in_hierarchy.greaters do
249 if super_mclassdef == mclassdef then continue # skip self
250 lst.add(super_mclassdef.mclass)
251 end
252 end
253 return lst
254 end
255
256 # Get direct children of 'self'.
257 fun children: Set[MClass] do
258 var lst = new HashSet[MClass]
259 for mclassdef in self.mclassdefs do
260 for sub_mclassdef in mclassdef.in_hierarchy.direct_smallers do
261 if sub_mclassdef == mclassdef then continue # skip self
262 lst.add(sub_mclassdef.mclass)
263 end
264 end
265 return lst
266 end
267
268 # Get all children of 'self'.
269 fun descendants: Set[MClass] do
270 var lst = new HashSet[MClass]
271 for mclassdef in self.mclassdefs do
272 for sub_mclassdef in mclassdef.in_hierarchy.smallers do
273 if sub_mclassdef == mclassdef then continue # skip self
274 lst.add(sub_mclassdef.mclass)
275 end
276 end
277 return lst
278 end
279
280 # Get the list of constructors available for 'self'.
281 fun constructors: Set[MMethod] do
282 var res = new HashSet[MMethod]
283 for mclassdef in mclassdefs do
284 for mpropdef in mclassdef.mpropdefs do
285 if mpropdef isa MMethodDef then
286 if mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
287 end
288 end
289 end
290 return res
291 end
292
293 # Get the list of methods introduced in 'self'.
294 fun intro_methods: Set[MMethod] do
295 var res = new HashSet[MMethod]
296 for mclassdef in mclassdefs do
297 for mpropdef in mclassdef.mpropdefs do
298 if mpropdef isa MMethodDef then
299 if mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
300 end
301 end
302 end
303 return res
304 end
305
306 # the set of properties introduced in 'self'.
307 fun intro_mproperties(min_visibility: MVisibility): Set[MProperty] do
308 var set = new HashSet[MProperty]
309 for mclassdef in mclassdefs do
310 for mprop in mclassdef.intro_mproperties do
311 if mprop.visibility < min_visibility then continue
312 set.add(mprop)
313 end
314 end
315 return set
316 end
317
318 fun intro_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
319 var set = new HashSet[MPropDef]
320 for mclassdef in mclassdefs do
321 for mpropdef in mclassdef.mpropdefs do
322 if not mpropdef.is_intro then continue
323 if mpropdef.mproperty.visibility < min_visibility then continue
324 set.add(mpropdef)
325 end
326 end
327 return set
328 end
329
330 # the set of locally refined properties in 'self'.
331 fun redef_mproperties(min_visibility: MVisibility): Set[MProperty] do
332 var set = new HashSet[MProperty]
333 for mclassdef in mclassdefs do
334 for mpropdef in mclassdef.mpropdefs do
335 if mpropdef.mproperty.visibility < min_visibility then continue
336 if mpropdef.mproperty.intro_mclassdef.mclass != self then set.add(mpropdef.mproperty)
337 end
338 end
339 return set
340 end
341
342 fun redef_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
343 var set = new HashSet[MPropDef]
344 for mclassdef in mclassdefs do
345 for mpropdef in mclassdef.mpropdefs do
346 if mpropdef.is_intro then continue
347 if mpropdef.mproperty.visibility < min_visibility then continue
348 set.add(mpropdef)
349 end
350 end
351 return set
352 end
353
354 # the set of methods inherited by 'self'.
355 fun inherited_mproperties(mainmodule: MModule, min_visibility: MVisibility): Set[MProperty] do
356 var set = new HashSet[MProperty]
357 for parent in in_hierarchy(mainmodule).direct_greaters do
358 set.add_all(parent.intro_mproperties(min_visibility))
359 set.add_all(parent.inherited_mproperties(mainmodule, min_visibility))
360 end
361 return set
362 end
363
364 # the set of introduced and redefined mproperties
365 fun local_mproperties(min_visibility: MVisibility): Set[MProperty] do
366 var set = new HashSet[MProperty]
367 set.add_all(intro_mproperties(min_visibility))
368 set.add_all(redef_mproperties(min_visibility))
369 return set
370 end
371
372 # the set of all accessible mproperties for this class
373 fun all_mproperties(mainmodule: MModule, min_visibility: MVisibility): Set[MProperty] do
374 var set = new HashSet[MProperty]
375 set.add_all(local_mproperties(min_visibility))
376 set.add_all(inherited_mproperties(mainmodule, min_visibility))
377 return set
378 end
379
380 # the set of all accessible mattributes for this class
381 fun all_mattributes(mainmodule: MModule, min_visibility: MVisibility): Set[MAttribute] do
382 var set = new HashSet[MAttribute]
383 for mprop in all_mproperties(mainmodule, min_visibility) do
384 if mprop isa MAttribute then set.add(mprop)
385 end
386 return set
387 end
388
389 # Get the list of locally refined methods in 'self'.
390 fun redef_methods: Set[MMethod] do
391 var res = new HashSet[MMethod]
392 for mclassdef in mclassdefs do
393 for mpropdef in mclassdef.mpropdefs do
394 if mpropdef isa MMethodDef then
395 if not mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty)
396 end
397 end
398 end
399 return res
400 end
401
402 fun inherited_methods: Set[MMethod] do
403 var res = new HashSet[MMethod]
404 for s in ancestors do
405 for m in s.intro_methods do
406 if not self.intro_methods.has(m) and not self.redef_methods.has(m) then res.add(m)
407 end
408 end
409 return res
410 end
411
412 # Get the list of all virtual types available in 'self'.
413 fun virtual_types: Set[MVirtualTypeProp] do
414 var res = new HashSet[MVirtualTypeProp]
415 for mclassdef in mclassdefs do
416 for mpropdef in mclassdef.mpropdefs do
417 if mpropdef isa MVirtualTypeDef then
418 res.add(mpropdef.mproperty)
419 end
420 end
421 end
422 for ancestor in ancestors do
423 for mclassdef in ancestor.mclassdefs do
424 for mpropdef in mclassdef.mpropdefs do
425 if mpropdef isa MVirtualTypeDef then
426 res.add(mpropdef.mproperty)
427 end
428 end
429 end
430 end
431 return res
432 end
433
434 # Get the list of all parameter types in 'self'.
435 fun parameter_types: Map[String, MType] do
436 var res = new HashMap[String, MType]
437 for p in mparameters do
438 res[p.name] = p
439 end
440 return res
441 end
442
443 fun is_class: Bool do
444 return self.kind == concrete_kind or self.kind == abstract_kind
445 end
446
447 fun is_interface: Bool do
448 return self.kind == interface_kind
449 end
450
451 fun is_enum: Bool do
452 return self.kind == enum_kind
453 end
454
455 fun is_abstract: Bool do
456 return self.kind == abstract_kind
457 end
458 end
459
460 redef class MAttribute
461 # Is this attribute nullable for sure?
462 #
463 # This mean that its introduction is declarred with a nullable static type
464 # since attributes are invariant this will work on most cases
465 # attributes with static type anchored with a virtual type are not "nullable for-sure"
466 # because this type can be redefined in subclasses
467 fun is_nullable: Bool do return intro.static_mtype isa MNullableType
468 end
469
470 redef class MClassDef
471 # modifiers are keywords like redef, private etc.
472 fun modifiers: Array[String] do
473 var res = new Array[String]
474 if not is_intro then
475 res.add "redef"
476 else
477 res.add mclass.visibility.to_s
478 end
479 res.add mclass.kind.to_s
480 return res
481 end
482
483 fun collect_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
484 var res = new HashSet[MPropDef]
485 for mpropdef in mpropdefs do
486 if mpropdef.mproperty.visibility < min_visibility then continue
487 res.add mpropdef
488 end
489 return res
490 end
491
492 fun collect_intro_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
493 var res = new HashSet[MPropDef]
494 for mpropdef in mpropdefs do
495 if not mpropdef.is_intro then continue
496 if mpropdef.mproperty.visibility < min_visibility then continue
497 res.add mpropdef
498 end
499 return res
500 end
501
502 fun collect_redef_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do
503 var res = new HashSet[MPropDef]
504 for mpropdef in mpropdefs do
505 if mpropdef.is_intro then continue
506 if mpropdef.mproperty.visibility < min_visibility then continue
507 res.add mpropdef
508 end
509 return res
510 end
511 end
512
513 redef class MPropDef
514 # modifiers are keywords like redef, private etc.
515 fun modifiers: Array[String] do
516 var res = new Array[String]
517 if not is_intro then
518 res.add "redef"
519 else
520 res.add mproperty.visibility.to_s
521 end
522 var mprop = self
523 if mprop isa MVirtualTypeDef then
524 res.add "type"
525 else if mprop isa MMethodDef then
526 if mprop.is_abstract then
527 res.add "abstract"
528 else if mprop.is_intern then
529 res.add "intern"
530 end
531 if mprop.mproperty.is_init then
532 res.add "init"
533 else
534 res.add "fun"
535 end
536 end
537 return res
538 end
539 end
540
541 # Sorters
542
543 # Sort mentities by their name
544 class MEntityNameSorter
545 super Comparator
546 redef type COMPARED: MEntity
547 redef fun compare(a, b) do return a.name <=> b.name
548 end
549
550 # Sort MConcerns based on the module importation hierarchy ranking
551 # see also: `MConcern::concern_rank` and `MConcern::booster_rank`
552 #
553 # Comparison is made with the formula:
554 #
555 # ~~~nitish
556 # a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
557 # ~~~
558 #
559 # If both `a` and `b` have the same ranking,
560 # ordering is based on lexicographic comparison of `a.name` and `b.name`
561 class MConcernRankSorter
562 super Comparator
563 redef type COMPARED: MConcern
564
565 redef fun compare(a, b) do
566 if a.concern_rank == b.concern_rank then
567 return a.name <=> b.name
568 end
569 return a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_rank
570 end
571 end