mmodule: Added support of package importation
[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 mpackage
21 private import more_collections
22
23 # The container class of a Nit object-oriented model.
24 #
25 # A model knows modules, classes and properties and can retrieve them.
26 #
27 # However, a model is not a program or a library as it can contains modules
28 # found by the system (including broken ones) but not used.
29 redef class Model
30 # All known modules
31 var mmodules = new Array[MModule]
32
33 # Full module importation hierarchy including private or nested links.
34 var mmodule_importation_hierarchy = new POSet[MModule]
35
36 # Collections of modules grouped by their short names
37 private var mmodules_by_name = new MultiHashMap[String, MModule]
38
39 # Return all module named `name`
40 # If such a module does not exist, null is returned (instead of an empty array)
41 #
42 # Visibility or modules are not considered
43 fun get_mmodules_by_name(name: String): nullable Array[MModule]
44 do
45 if mmodules_by_name.has_key(name) then
46 return mmodules_by_name[name]
47 else
48 return null
49 end
50 end
51 end
52
53 redef class MGroup
54 # The loaded modules of this group
55 var mmodules = new Array[MModule]
56
57 # The default module of a group (if any, and if loaded)
58 #
59 # The default module of a group is the one that has the same name.
60 # Return `null` if the group has no default module or if the default
61 # module is not loaded.
62 var default_mmodule: nullable MModule = null
63
64 redef fun mdoc_or_fallback
65 do
66 if mdoc != null then return mdoc
67 var default_mmodule = self.default_mmodule
68 if default_mmodule == null then return null
69 return default_mmodule.mdoc_or_fallback
70 end
71 end
72
73 # A Nit module is usually associated with a Nit source file.
74 class MModule
75 super MConcern
76
77 # The model considered
78 redef var model
79
80 # The group of module in the package if any
81 var mgroup: nullable MGroup
82
83 # The path of the module source, if any
84 #
85 # safe alias to `location.file.filepath`
86 fun filepath: nullable String do
87 var res = self.location.file
88 if res == null then return null
89 return res.filename
90 end
91
92 # The package of the module if any
93 # Safe alias for `mgroup.mpackage`
94 fun mpackage: nullable MPackage
95 do
96 var g = mgroup
97 if g == null then return null else return g.mpackage
98 end
99
100 # The short name of the module
101 redef var name
102
103 redef var location is writable
104
105 # Alias for `name`
106 redef fun to_s do return self.name
107
108 # The view of the module in the `model.mmodule_importation_hierarchy`
109 var in_importation: POSetElement[MModule] is noinit
110
111 # The canonical name of the module.
112 #
113 # It is usually the `name` prefixed by the package's name.
114 # Example: `"package::name"`
115 #
116 # Default modules use a doubled name to distinguish them from the package name.
117 # E.g.: `"core::core"`
118 #
119 # If the module is package-less, then the short-name is used alone.
120 redef var full_name is lazy do
121 var mgroup = self.mgroup
122 if mgroup == null then
123 return self.name
124 else
125 return "{mgroup.mpackage.name}::{self.name}"
126 end
127 end
128
129 # The namespace used for entities according to their visibility `v`.
130 #
131 # Public entities use only the package as a namespace.
132 # Private entities use the `full_name` (i.e. "package::module")
133 #
134 # This method is used by entities to implement their `full_name`.
135 fun namespace_for(v: MVisibility): String do
136 if v <= private_visibility then return full_name
137 var mgroup = self.mgroup
138 if mgroup == null then
139 return full_name
140 else
141 return mgroup.mpackage.full_name
142 end
143 end
144
145 # Return the name of the global C identifier associated to `self`.
146 # This name is used to prefix files and other C identifiers associated with `self`.
147 redef var c_name is lazy do
148 var g = mgroup
149 var res
150 if g != null and g.mpackage.name != name then
151 res = g.mpackage.name.to_cmangle + "__" + name.to_cmangle
152 else
153 res = name.to_cmangle
154 end
155 return res
156 end
157
158 # C identifier version of `namespace_for`.
159 # See `c_name`
160 #
161 # This method is used by entities to implement their `c_name`.
162 fun c_namespace_for(v: MVisibility): String do
163 if v <= private_visibility then return c_name
164 var mgroup = self.mgroup
165 if mgroup == null then
166 return c_name
167 else
168 return mgroup.mpackage.c_name
169 end
170 end
171
172 # Create a new empty module and register it to a model
173 init
174 do
175 model.mmodules_by_name.add_one(name, self)
176 model.mmodules.add(self)
177 var mgroup = self.mgroup
178 if mgroup != null then
179 mgroup.mmodules.add(self)
180 if mgroup.name == name then
181 assert mgroup.default_mmodule == null
182 mgroup.default_mmodule = self
183 end
184 end
185 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
186 end
187
188 # Register the imported modules (ie "import some_module") and packages importation graph
189 # In the same time it register the imported package
190 # The visibility must be set with `set_visibility_for`.
191 fun set_imported_mmodules(imported_mmodules: Array[MModule])
192 do
193 for m in imported_mmodules do
194 self.model.mmodule_importation_hierarchy.add_edge(self, m)
195 var actual_mpackage = self.mpackage
196 var imported_mpackage = m.mpackage
197 if actual_mpackage != null and imported_mpackage != null then
198 # Register the imported package
199 self.model.mpackage_importation_graph.add_arc(actual_mpackage, imported_mpackage)
200 end
201 end
202 end
203
204 private var intrude_mmodules = new HashSet[MModule]
205 private var public_mmodules = new HashSet[MModule]
206 private var private_mmodules = new HashSet[MModule]
207
208 # Return the visibility level of an imported module `m`
209 fun visibility_for(m: MModule): MVisibility
210 do
211 if m == self then return intrude_visibility
212 if self.intrude_mmodules.has(m) then return intrude_visibility
213 if self.public_mmodules.has(m) then return public_visibility
214 if self.private_mmodules.has(m) then return private_visibility
215 return none_visibility
216 end
217
218 # Set the visibility of an imported module
219 # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
220 fun set_visibility_for(m: MModule, v: MVisibility)
221 do
222 if v == intrude_visibility then
223 self.intrude_mmodules.add(m)
224 self.intrude_mmodules.add_all(m.intrude_mmodules)
225 self.public_mmodules.add_all(m.public_mmodules)
226 self.private_mmodules.add_all(m.private_mmodules)
227 else if v == public_visibility then
228 self.public_mmodules.add(m)
229 self.public_mmodules.add_all(m.intrude_mmodules)
230 self.public_mmodules.add_all(m.public_mmodules)
231 else if v == private_visibility then
232 self.private_mmodules.add(m)
233 self.private_mmodules.add_all(m.intrude_mmodules)
234 self.private_mmodules.add_all(m.public_mmodules)
235 else
236 print "{self} visibility for {m} = {v}"
237 abort # invalid visibility
238 end
239 end
240
241 # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
242 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
243 do
244 var v = visibility_for(intro_mmodule)
245 if v == intrude_visibility then
246 return visibility >= private_visibility
247 else if v == public_visibility then
248 return visibility > private_visibility
249 else if v == private_visibility then
250 return visibility > private_visibility
251 else if v == none_visibility then
252 return false
253 else
254 abort
255 end
256 end
257
258 # Is `self` a module generated by a tool?
259 #
260 # This flag has no effect on the semantic.
261 # It is only intended on software engineering software to discriminate computer-generated modules from human-written ones.
262 var is_generated: Bool = false is writable
263
264 # Get the non-`is_fictive` module on which `self` is based on.
265 #
266 # On non-fictive module, this returns `self`.
267 # On fictive modules, this is used to refer the module which `self` is based on.
268 #
269 # This attribute should be set when a fictive module is created. See `is_fictive`.
270 var first_real_mmodule: MModule = self is writable
271
272 redef fun parent_concern do return mgroup
273 end