da881a0c4f4459b0b8d4a2f62f746acee8e95734
[nit.git] / src / model / model_visitor.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 # Simple visitor framework for Nit models.
16 #
17 # This module provides the `ModelVisitor` abstract class and
18 # refines the classes of the `MEntity` hierarchy to add visiting related methods.
19 #
20 # A standard approach on complex visitor is to dispatch the `ModelVisitor::visit` on the
21 # entities to do specific things.
22 #
23 # ~~~nitish
24 # class FooVisitor
25 # super ModelVisitor
26 # redef fun visit(e) do e.foo_visit(self)
27 # end
28 #
29 # redef class MEntity
30 # do
31 # fun foo_vist(v: FooVisitor) do visit_all(v)
32 # end
33 #
34 # redef class MClass
35 # do
36 # redef fun foo_visit(v) do
37 # print self
38 # super
39 # end
40 # end
41 # ~~~
42 module model_visitor
43
44 import model
45
46 # The abstract model visitor template.
47 #
48 # Specific visitor must implement the `visit` method to perform the work.
49 abstract class ModelVisitor
50 # Visit the entity `e`.
51 #
52 # This method setups `current_entity` and call `visit`.
53 # If `e` is null, nothing is done.
54 fun enter_visit(e: nullable MEntity) do
55 if e == null then return
56 if not accept_mentity(e) then return
57 var old_entity = current_entity
58 current_entity = e
59 visit(e)
60 current_entity = old_entity
61 end
62
63 # The current visited entity
64 var current_entity: nullable MEntity = null
65
66 # Method to define in specific visitor.
67 #
68 # It should not be called directly but used by `enter_visit`
69 protected fun visit(e: MEntity) is abstract
70
71 # Filter classes and method on the visibility.
72 #
73 # If set, only the classes and method with at least the given
74 # visibility level will be visited.
75 var min_visibility: nullable MVisibility = null is writable
76
77 # Can we accept this `mentity` in the view regarding its visibility?
78 fun accept_visibility(mentity: MEntity): Bool do
79 return mentity.accept_visibility(min_visibility)
80 end
81
82 # Include fictive entities?
83 #
84 # By default, fictive entities (see `MEntity::is_fictive`) are not visited.
85 var include_fictive = false is writable
86
87 # Can we accept this `mentity` in the view regarding its fictivity?
88 fun accept_fictive(mentity: MEntity): Bool do
89 if include_fictive then return true
90 return not mentity.is_fictive
91 end
92
93 # Should we accept mentities with empty documentation?
94 #
95 # Default is `true`.
96 var include_empty_doc = true is writable
97
98 # Can we accept this `mentity` regarding its documentation?
99 fun accept_empty_doc(mentity: MEntity): Bool do
100 if include_empty_doc then return true
101 return mentity.mdoc != null
102 end
103
104 # Should we accept nitunit test suites?
105 #
106 # Default is `false`.
107 var include_test_suite = false is writable
108
109 # Can we accept this `mentity` regarding its test suite status?
110 fun accept_test_suite(mentity: MEntity): Bool do
111 if include_test_suite then return true
112 if not mentity isa MModule then return true
113 return not mentity.is_test_suite
114 end
115
116 # Should we accept `MAttribute` instances?
117 #
118 # Default is `true`.
119 var include_attribute = true is writable
120
121 # Can we accept this `mentity` regarding its type?
122 fun accept_attribute(mentity: MEntity): Bool do
123 if include_attribute then return true
124 if mentity isa MAttribute then return false
125 if mentity isa MAttributeDef then return false
126 return true
127 end
128
129 # Should we accept this `mentity` from the view?
130 fun accept_mentity(mentity: MEntity): Bool do
131 if not accept_visibility(mentity) then return false
132 if not accept_fictive(mentity) then return false
133 if not accept_empty_doc(mentity) then return false
134 if not accept_test_suite(mentity) then return false
135 if not accept_attribute(mentity) then return false
136 return true
137 end
138
139 end
140
141 redef class MEntity
142 # Call `v.enter_visit` on all nested entities.
143 #
144 # See the specific implementation in the subclasses.
145 fun visit_all(v: ModelVisitor) do end
146
147 private fun accept_visibility(min_visibility: nullable MVisibility): Bool do return true
148 end
149
150 redef class Model
151 # Visit all the packages of the model.
152 redef fun visit_all(v) do
153 for x in mpackages do v.enter_visit(x)
154 end
155 end
156
157 redef class MPackage
158 # Visit the root group of the package.
159 redef fun visit_all(v) do
160 v.enter_visit(root)
161 end
162 end
163
164 redef class MGroup
165 # Visit all the subgroups and modules of the group.
166 redef fun visit_all(v) do
167 for x in in_nesting.direct_smallers do v.enter_visit(x)
168 for x in mmodules do v.enter_visit(x)
169 end
170 end
171
172 redef class MModule
173 # Visit all the classes and class definitions of the module.
174 #
175 # On class introduction, the `MClass` then the `MClassDef` are visited.
176 # On class refinement, only the `MClassDef` is visited (the `MClass` is visited in an imported module).
177 # On class importation, nothing is visited (the `MClass` and the `MClassDef` are visited in imported modules).
178 redef fun visit_all(v) do
179 for x in mclassdefs do
180 if x.is_intro then v.enter_visit(x.mclass)
181 v.enter_visit(x)
182 end
183 end
184 end
185
186 redef class MClass
187 redef fun accept_visibility(min_visibility) do
188 if min_visibility == null then return true
189 return visibility >= min_visibility
190 end
191 end
192
193 redef class MClassDef
194 # Visit all the classes and class definitions of the module.
195 #
196 # On property introduction, the `MProperty` then the `MPropDef` are visited.
197 # On property redefinition, only the `MPropDef` is visited (the `MProperty` is visited in an inherited class).
198 # On property inheritance, nothing is visited (the `MProperty` and the `MPropDef` are visited in inherited classes).
199 redef fun visit_all(v) do
200 for x in mpropdefs do
201 if x.is_intro then v.enter_visit(x.mproperty)
202 v.enter_visit(x)
203 end
204 end
205
206 redef fun accept_visibility(min_visibility) do
207 if min_visibility == null then return true
208 return mclass.visibility >= min_visibility
209 end
210 end
211
212 redef class MProperty
213 redef fun accept_visibility(min_visibility) do
214 if min_visibility == null then return true
215 return visibility >= min_visibility
216 end
217 end
218
219 redef class MPropDef
220 redef fun accept_visibility(min_visibility) do
221 if min_visibility == null then return true
222 return mproperty.visibility >= min_visibility
223 end
224 end