596ea9d1e6ab8e0b6f2fc5a88a769b3af5c9dd69
[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: Array[MModule] = new Array[MModule]
29
30 # placebo for old module nesting hierarchy.
31 # where mainmodule < mainmodule::nestedmodule
32 #
33 # TODO REMOVE, rely on mgroup instead
34 var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
35
36 # Full module importation hierarchy including private or nested links.
37 var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
38
39 # Collections of modules grouped by their short names
40 private var mmodules_by_name: MultiHashMap[String, MModule] = new MultiHashMap[String, MModule]
41
42 # Return all module named `name`
43 # If such a module does not exist, null is returned (instead of an empty array)
44 #
45 # Visibility or modules are not considered
46 fun get_mmodules_by_name(name: String): nullable Array[MModule]
47 do
48 if mmodules_by_name.has_key(name) then
49 return mmodules_by_name[name]
50 else
51 return null
52 end
53 end
54 end
55
56 redef class MGroup
57 # The loaded modules of this group
58 var mmodules = new Array[MModule]
59
60 # Placebo stuff to find the owner (module with same name)
61 # null is returned if there is no owner, or if it is not loaded yet
62 fun fuzzy_owner: nullable MModule
63 do
64 for m in mmodules do if m.name == name then return m
65 return null
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 # placebo for old module nesting hierarchy
77 # return null if self is not nested (ie. is a top-level module)
78 #
79 # TODO REMOVE, rely on mgroup instead
80 var direct_owner: nullable MModule
81
82 # The group of module in the project if any
83 var mgroup: nullable MGroup
84
85 # The short name of the module
86 redef var name: String
87
88 # The origin of the definition
89 var location: Location
90
91 # Alias for `name`
92 redef fun to_s do return self.name
93
94 # placebo for old module nesting hierarchy
95 # The view of the module in the `model.mmodule_nesting_hierarchy`
96 #
97 # TODO REMOVE, rely on mgroup instead
98 var in_nesting: POSetElement[MModule]
99
100 # The view of the module in the `model.mmodule_importation_hierarchy`
101 var in_importation: POSetElement[MModule]
102
103 # The canonical name of the module
104 # Example: `"project::name"`
105 fun full_name: String
106 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 # Create a new empty module and register it to a model
116 init(model: Model, mgroup: nullable MGroup, name: String, location: Location)
117 do
118 self.model = model
119 self.name = name
120 self.location = location
121 model.mmodules_by_name.add_one(name, self)
122 model.mmodules.add(self)
123 self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
124 self.mgroup = mgroup
125 if mgroup != null then
126 mgroup.mmodules.add(self)
127 # placebo for old module nesting hierarchy
128 var direct_owner = mgroup.fuzzy_owner
129 if direct_owner == self then
130 # The module is the new owner of its own group, thus adopt the other modules
131 for m in mgroup.mmodules do
132 if m == self then continue
133 m.direct_owner = self
134 model.mmodule_nesting_hierarchy.add_edge(self, m)
135 end
136 # The potential owner is the the fuzzy_owner of the parent group
137 if mgroup.parent != null then direct_owner = mgroup.parent.fuzzy_owner
138 end
139 if direct_owner != self and direct_owner != null then
140 self.direct_owner = direct_owner
141 model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
142 end
143 end
144 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
145 end
146
147 # Register the imported modules (ie "import some_module")
148 # This function can only invoked once by mmodule.
149 # The visibility must be set with `set_visibility_for`.
150 fun set_imported_mmodules(imported_mmodules: Array[MModule])
151 do
152 assert unique_invocation: self.in_importation.direct_greaters.is_empty
153 for m in imported_mmodules do
154 self.model.mmodule_importation_hierarchy.add_edge(self, m)
155 end
156 end
157
158 private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
159 private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
160 private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
161
162 # Return the visibility level of an imported module `m`
163 fun visibility_for(m: MModule): MVisibility
164 do
165 if m == self then return intrude_visibility
166 if self.intrude_mmodules.has(m) then return intrude_visibility
167 if self.public_mmodules.has(m) then return public_visibility
168 if self.private_mmodules.has(m) then return private_visibility
169 return none_visibility
170 end
171
172 # Set the visibility of an imported module
173 # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
174 fun set_visibility_for(m: MModule, v: MVisibility)
175 do
176 if v == intrude_visibility then
177 self.intrude_mmodules.add(m)
178 self.intrude_mmodules.add_all(m.intrude_mmodules)
179 self.public_mmodules.add_all(m.public_mmodules)
180 self.private_mmodules.add_all(m.private_mmodules)
181 else if v == public_visibility then
182 self.public_mmodules.add(m)
183 self.public_mmodules.add_all(m.intrude_mmodules)
184 self.public_mmodules.add_all(m.public_mmodules)
185 else if v == private_visibility then
186 self.private_mmodules.add(m)
187 self.private_mmodules.add_all(m.intrude_mmodules)
188 self.private_mmodules.add_all(m.public_mmodules)
189 else
190 print "{self} visibility for {m} = {v}"
191 abort # invalid visibility
192 end
193 end
194
195 # placebo for old module nesting hierarchy
196 fun public_owner: nullable MModule
197 do
198 var mgroup = self.mgroup
199 if mgroup == null then return null
200 mgroup = mgroup.mproject.root
201 if mgroup.mmodules.is_empty then return null
202 var res = mgroup.fuzzy_owner
203 if res == self then return null
204 return res
205 end
206
207 # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
208 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
209 do
210 var v = visibility_for(intro_mmodule)
211 if v == intrude_visibility then
212 return visibility >= private_visibility
213 else if v == public_visibility then
214 return visibility > private_visibility
215 else if v == private_visibility then
216 return visibility > private_visibility
217 else if v == none_visibility then
218 return false
219 else
220 abort
221 end
222 end
223
224 # Is the mmodule created for internal purpose?
225 # Fictive module are instantied internally but they should not be
226 # exposed to the final user
227 var is_fictive: Bool writable = false
228
229 redef fun parent_concern do return mgroup
230 end