e5a740966a899e5f0dcdae39e75a6584c90cdb32
[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_filters
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
51 # Visit the entity `e`.
52 #
53 # This method setups `current_entity` and call `visit`.
54 # If `e` is null, nothing is done.
55 fun enter_visit(e: nullable MEntity) do
56 if e == null then return
57 if not accept_mentity(e) then return
58 var old_entity = current_entity
59 current_entity = e
60 visit(e)
61 current_entity = old_entity
62 end
63
64 # The current visited entity
65 var current_entity: nullable MEntity = null
66
67 # Method to define in specific visitor.
68 #
69 # It should not be called directly but used by `enter_visit`
70 protected fun visit(e: MEntity) is abstract
71
72 # Filters to apply when visiting the model.
73 #
74 # See ModelFilters for configuration.
75 var filter: ModelFilter is lazy, writable, optional do
76 return new ModelFilter(
77 min_visibility = protected_visibility,
78 accept_fictive = false,
79 accept_test = false,
80 accept_redef = true,
81 accept_extern = true,
82 accept_attribute = true,
83 accept_empty_doc = true
84 )
85 end
86
87 # Should we accept this `mentity` from the view?
88 #
89 # If no `override_filter` is passed then use `self.filter`.
90 fun accept_mentity(mentity: MEntity, override_filter: nullable ModelFilter): Bool do
91 if override_filter != null then
92 return override_filter.accept_mentity(mentity)
93 end
94 return filter.accept_mentity(mentity)
95 end
96 end
97
98 redef class MEntity
99 # Call `v.enter_visit` on all nested entities.
100 #
101 # See the specific implementation in the subclasses.
102 fun visit_all(v: ModelVisitor) do end
103 end
104
105 redef class Model
106 # Visit all the packages of the model.
107 redef fun visit_all(v) do
108 for x in mpackages do v.enter_visit(x)
109 end
110 end
111
112 redef class MPackage
113 # Visit the root group of the package.
114 redef fun visit_all(v) do
115 v.enter_visit(root)
116 end
117 end
118
119 redef class MGroup
120 # Visit all the subgroups and modules of the group.
121 redef fun visit_all(v) do
122 for x in in_nesting.direct_smallers do v.enter_visit(x)
123 for x in mmodules do v.enter_visit(x)
124 end
125 end
126
127 redef class MModule
128 # Visit all the classes and class definitions of the module.
129 #
130 # On class introduction, the `MClass` then the `MClassDef` are visited.
131 # On class refinement, only the `MClassDef` is visited (the `MClass` is visited in an imported module).
132 # On class importation, nothing is visited (the `MClass` and the `MClassDef` are visited in imported modules).
133 redef fun visit_all(v) do
134 for x in mclassdefs do
135 if x.is_intro then v.enter_visit(x.mclass)
136 v.enter_visit(x)
137 end
138 end
139 end
140
141 redef class MClassDef
142 # Visit all the classes and class definitions of the module.
143 #
144 # On property introduction, the `MProperty` then the `MPropDef` are visited.
145 # On property redefinition, only the `MPropDef` is visited (the `MProperty` is visited in an inherited class).
146 # On property inheritance, nothing is visited (the `MProperty` and the `MPropDef` are visited in inherited classes).
147 redef fun visit_all(v) do
148 for x in mpropdefs do
149 if x.is_intro then v.enter_visit(x.mproperty)
150 v.enter_visit(x)
151 end
152 end
153 end