model: intro `MModule::first_real_mmodule` to get the first non-fictive module
[nit.git] / src / model / mmodule.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 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 # modules and module hierarchies in the metamodel
18 module mmodule
19
20 import location
21 import mproject
22 private import more_collections
23
24 # The container class of a Nit object-oriented model.
25 # A model knows modules, classes and properties and can retrieve them.
26 redef class Model
27 # All known modules
28 var mmodules = new Array[MModule]
29
30 # Full module importation hierarchy including private or nested links.
31 var mmodule_importation_hierarchy = new POSet[MModule]
32
33 # Collections of modules grouped by their short names
34 private var mmodules_by_name = new MultiHashMap[String, MModule]
35
36 # Return all module named `name`
37 # If such a module does not exist, null is returned (instead of an empty array)
38 #
39 # Visibility or modules are not considered
40 fun get_mmodules_by_name(name: String): nullable Array[MModule]
41 do
42 if mmodules_by_name.has_key(name) then
43 return mmodules_by_name[name]
44 else
45 return null
46 end
47 end
48 end
49
50 redef class MGroup
51 # The loaded modules of this group
52 var mmodules = new Array[MModule]
53
54 # The default module of a group (if any, and if loaded)
55 #
56 # The default module of a group is the one that has the same name.
57 # Return `null` if the group has no default module or if the default
58 # module is not loaded.
59 var default_mmodule: nullable MModule = null
60
61 redef fun mdoc_or_fallback
62 do
63 if mdoc != null then return mdoc
64 if default_mmodule == null then return null
65 return default_mmodule.mdoc_or_fallback
66 end
67 end
68
69 # A Nit module is usually associated with a Nit source file.
70 class MModule
71 super MConcern
72
73 # The model considered
74 redef var model: Model
75
76 # The group of module in the project if any
77 var mgroup: nullable MGroup
78
79 # The project of the module if any
80 # Safe alias for `mgroup.mproject`
81 fun mproject: nullable MProject
82 do
83 var g = mgroup
84 if g == null then return null else return g.mproject
85 end
86
87 # The short name of the module
88 redef var name: String
89
90 # The origin of the definition
91 var location: Location
92
93 # Alias for `name`
94 redef fun to_s do return self.name
95
96 # The view of the module in the `model.mmodule_importation_hierarchy`
97 var in_importation: POSetElement[MModule] is noinit
98
99 # The canonical name of the module.
100 #
101 # It is usually the `name` prefixed by the project's name.
102 # Example: `"project::name"`
103 #
104 # If both names are the same (of if the module is project-less), then
105 # the short-name is used alone.
106 redef var full_name is lazy do
107 var mgroup = self.mgroup
108 if mgroup == null or mgroup.mproject.name == self.name then
109 return self.name
110 else
111 return "{mgroup.mproject.name}::{self.name}"
112 end
113 end
114
115 # The namespace used for entities according to their visibility `v`.
116 #
117 # Public entities use only the project as a namespace.
118 # Private entities use the `full_name` (i.e. "project::module")
119 #
120 # This method is used by entities to implement their `full_name`.
121 fun namespace_for(v: MVisibility): String do
122 if v <= private_visibility then return full_name
123 var mgroup = self.mgroup
124 if mgroup == null then
125 return full_name
126 else
127 return mgroup.mproject.full_name
128 end
129 end
130
131 # Return the name of the global C identifier associated to `self`.
132 # This name is used to prefix files and other C identifiers associated with `self`.
133 redef var c_name: String is lazy do
134 var g = mgroup
135 var res
136 if g != null and g.mproject.name != name then
137 res = g.mproject.name.to_cmangle + "__" + name.to_cmangle
138 else
139 res = name.to_cmangle
140 end
141 return res
142 end
143
144 # C identifier version of `namespace_for`.
145 # See `c_name`
146 #
147 # This method is used by entities to implement their `c_name`.
148 fun c_namespace_for(v: MVisibility): String do
149 if v <= private_visibility then return c_name
150 var mgroup = self.mgroup
151 if mgroup == null then
152 return c_name
153 else
154 return mgroup.mproject.c_name
155 end
156 end
157
158 # Create a new empty module and register it to a model
159 init
160 do
161 model.mmodules_by_name.add_one(name, self)
162 model.mmodules.add(self)
163 if mgroup != null then
164 mgroup.mmodules.add(self)
165 if mgroup.name == name then
166 assert mgroup.default_mmodule == null
167 mgroup.default_mmodule = self
168 end
169 end
170 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
171 end
172
173 # Register the imported modules (ie "import some_module")
174 # This function can only invoked once by mmodule.
175 # The visibility must be set with `set_visibility_for`.
176 fun set_imported_mmodules(imported_mmodules: Array[MModule])
177 do
178 assert unique_invocation: self.in_importation.direct_greaters.is_empty
179 for m in imported_mmodules do
180 self.model.mmodule_importation_hierarchy.add_edge(self, m)
181 end
182 end
183
184 private var intrude_mmodules = new HashSet[MModule]
185 private var public_mmodules = new HashSet[MModule]
186 private var private_mmodules = new HashSet[MModule]
187
188 # Return the visibility level of an imported module `m`
189 fun visibility_for(m: MModule): MVisibility
190 do
191 if m == self then return intrude_visibility
192 if self.intrude_mmodules.has(m) then return intrude_visibility
193 if self.public_mmodules.has(m) then return public_visibility
194 if self.private_mmodules.has(m) then return private_visibility
195 return none_visibility
196 end
197
198 # Set the visibility of an imported module
199 # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
200 fun set_visibility_for(m: MModule, v: MVisibility)
201 do
202 if v == intrude_visibility then
203 self.intrude_mmodules.add(m)
204 self.intrude_mmodules.add_all(m.intrude_mmodules)
205 self.public_mmodules.add_all(m.public_mmodules)
206 self.private_mmodules.add_all(m.private_mmodules)
207 else if v == public_visibility then
208 self.public_mmodules.add(m)
209 self.public_mmodules.add_all(m.intrude_mmodules)
210 self.public_mmodules.add_all(m.public_mmodules)
211 else if v == private_visibility then
212 self.private_mmodules.add(m)
213 self.private_mmodules.add_all(m.intrude_mmodules)
214 self.private_mmodules.add_all(m.public_mmodules)
215 else
216 print "{self} visibility for {m} = {v}"
217 abort # invalid visibility
218 end
219 end
220
221 # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
222 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
223 do
224 var v = visibility_for(intro_mmodule)
225 if v == intrude_visibility then
226 return visibility >= private_visibility
227 else if v == public_visibility then
228 return visibility > private_visibility
229 else if v == private_visibility then
230 return visibility > private_visibility
231 else if v == none_visibility then
232 return false
233 else
234 abort
235 end
236 end
237
238 # Is `self` created for internal purpose?
239 # Fictive modules are instantiated internally but they should not be
240 # exposed to the final user.
241 var is_fictive: Bool = false is writable
242
243 # Is `self` a unit test module used by `nitunit`?
244 var is_test_suite: Bool = false is writable
245
246 # Get the first non `is_fictive` module greater than self
247 fun first_real_mmodule: MModule
248 do
249 var mmodule = self
250 while mmodule.is_fictive do
251 mmodule = mmodule.in_importation.direct_greaters.first
252 end
253 return mmodule
254 end
255
256 redef fun parent_concern do return mgroup
257 end