a3ee105bd62f1a39cae995fd73b71ec8e549a4f4
[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 # Module nesting hierarchy.
32 # where mainmodule < mainmodule::nestedmodule
33 var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
34
35 # Full module importation hierarchy including private or nested links.
36 var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
37
38 # Collections of modules grouped by their short names
39 private var mmodules_by_name: MultiHashMap[String, MModule] = new MultiHashMap[String, MModule]
40
41 # Return all module named `name`
42 # If such a module does not exist, null is returned (instead of an empty array)
43 #
44 # Visibility or modules are not considered
45 fun get_mmodules_by_name(name: String): nullable Array[MModule]
46 do
47 if mmodules_by_name.has_key(name) then
48 return mmodules_by_name[name]
49 else
50 return null
51 end
52 end
53 end
54
55 redef class MGroup
56 # The loaded modules of this group
57 var mmodules = new Array[MModule]
58 end
59
60 # A Nit module is usually associated with a Nit source file.
61 class MModule
62 # The model considered
63 var model: Model
64
65 # The direct nesting module, return null if self is not nested (ie. is a top-level module)
66 var direct_owner: nullable MModule
67
68 # The group of module in the project if any
69 var mgroup: nullable MGroup
70
71 # The short name of the module
72 var name: String
73
74 # The origin of the definition
75 var location: Location
76
77 # Alias for `name`
78 redef fun to_s do return self.name
79
80 # The view of the module in the `model.mmodule_nesting_hierarchy`
81 var in_nesting: POSetElement[MModule]
82
83 # The view of the module in the `model.mmodule_importation_hierarchy`
84 var in_importation: POSetElement[MModule]
85
86 # The canonical name of the module
87 # Example: `"project::name"`
88 fun full_name: String
89 do
90 var mgroup = self.mgroup
91 if mgroup == null or mgroup.mproject.name == self.name then
92 return self.name
93 else
94 return "{mgroup.mproject.name}::{self.name}"
95 end
96 end
97
98 # Create a new empty module and register it to a model
99 # `direct_owner` is the direct owner (null if top-level module)
100 init(model: Model, direct_owner: nullable MModule, name: String, location: Location)
101 do
102 self.model = model
103 self.name = name
104 self.location = location
105 model.mmodules_by_name.add_one(name, self)
106 model.mmodules.add(self)
107 self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
108 self.direct_owner = direct_owner
109 if direct_owner != null then
110 model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
111 end
112 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
113 end
114
115 # Register the imported modules (ie "import some_module")
116 # This function can only invoked once by mmodule.
117 # The visibility must be set with `set_visibility_for`.
118 fun set_imported_mmodules(imported_mmodules: Array[MModule])
119 do
120 assert unique_invocation: self.in_importation.direct_greaters.is_empty
121 for m in imported_mmodules do
122 self.model.mmodule_importation_hierarchy.add_edge(self, m)
123 end
124 end
125
126 private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
127 private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
128 private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
129
130 # Return the visibility level of an imported module `m`
131 fun visibility_for(m: MModule): MVisibility
132 do
133 if m == self then return intrude_visibility
134 if self.intrude_mmodules.has(m) then return intrude_visibility
135 if self.public_mmodules.has(m) then return public_visibility
136 if self.private_mmodules.has(m) then return private_visibility
137 return none_visibility
138 end
139
140 # Set the visibility of an imported module
141 # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
142 fun set_visibility_for(m: MModule, v: MVisibility)
143 do
144 if v == intrude_visibility then
145 self.intrude_mmodules.add(m)
146 self.intrude_mmodules.add_all(m.intrude_mmodules)
147 self.public_mmodules.add_all(m.public_mmodules)
148 self.private_mmodules.add_all(m.private_mmodules)
149 else if v == public_visibility then
150 self.public_mmodules.add(m)
151 self.public_mmodules.add_all(m.intrude_mmodules)
152 self.public_mmodules.add_all(m.public_mmodules)
153 else if v == private_visibility then
154 self.private_mmodules.add(m)
155 self.private_mmodules.add_all(m.intrude_mmodules)
156 self.private_mmodules.add_all(m.public_mmodules)
157 else
158 print "{self} visibility for {m} = {v}"
159 abort # invalid visibility
160 end
161 end
162
163 # The first module in the nesting hierarchy to export self as public
164 # This function is used to determine the canonical name of modules, classes and properties.
165 # REQUIRE: the visibility of all nesting modules is already set.
166 fun public_owner: nullable MModule
167 do
168 var res = self.direct_owner
169 var last = res
170 while last != null do
171 if last.visibility_for(self) >= public_visibility then res = last
172 last = last.direct_owner
173 end
174 return res
175 end
176
177 # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
178 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
179 do
180 var v = visibility_for(intro_mmodule)
181 if v == intrude_visibility then
182 return visibility >= private_visibility
183 else if v == public_visibility then
184 return visibility > private_visibility
185 else if v == private_visibility then
186 return visibility > private_visibility
187 else if v == none_visibility then
188 return false
189 else
190 abort
191 end
192 end
193 end