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