model: fix typo and style in documentation
[nit.git] / src / model / model_base.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 module model_base
18
19 import poset
20 import location
21 private import more_collections
22
23 # The container class of a Nit object-oriented model.
24 # A model knows modules, classes and properties and can retrieve them.
25 class Model
26 # All known modules
27 var mmodules: Array[MModule] = new Array[MModule]
28
29 # Module nesting hierarchy.
30 # where mainmodule < mainmodule::nestedmodule
31 var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
32
33 # Full module importation hierarchy including private or nested links.
34 var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
35
36 # Collections of modules grouped by their short names
37 private var mmodules_by_name: MultiHashMap[String, MModule] = 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 # A Nit module is usually associated with a Nit source file.
54 # Modules can be nested (see `direct_owner`, `public_owner`, and `in_nesting`)
55 class MModule
56 # The model considered
57 var model: Model
58
59 # The direct nesting module, return null if self is not nested (ie. is a top-level module)
60 var direct_owner: nullable MModule
61
62 # The short name of the module
63 var name: String
64
65 # The origin of the definition
66 var location: Location
67
68 # Alias for `name`
69 redef fun to_s do return self.name
70
71 # The view of the module in the `model.mmodule_nesting_hierarchy`
72 var in_nesting: POSetElement[MModule]
73
74 # The view of the module in the `model.mmodule_importation_hierarchy`
75 var in_importation: POSetElement[MModule]
76
77 # The canonical name of the module
78 # Example: `"owner::name"`
79 fun full_name: String
80 do
81 var owner = self.public_owner
82 if owner == null then
83 return self.name
84 else
85 return "{owner.full_name}::{self.name}"
86 end
87 end
88
89 # Create a new empty module and register it to a model
90 # `direct_owner` is the direct owner (null if top-level module)
91 init(model: Model, direct_owner: nullable MModule, name: String, location: Location)
92 do
93 self.model = model
94 self.name = name
95 self.location = location
96 model.mmodules_by_name.add_one(name, self)
97 model.mmodules.add(self)
98 self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
99 self.direct_owner = direct_owner
100 if direct_owner != null then
101 model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
102 end
103 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
104 end
105
106 # Register the imported modules (ie "import some_module")
107 # This function can only invoked once by mmodule.
108 # The visibility must be set with `set_visibility_for`.
109 fun set_imported_mmodules(imported_mmodules: Array[MModule])
110 do
111 assert unique_invocation: self.in_importation.direct_greaters.is_empty
112 for m in imported_mmodules do
113 self.model.mmodule_importation_hierarchy.add_edge(self, m)
114 end
115 end
116
117 private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
118 private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
119 private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
120
121 # Return the visibility level of an imported module `m`
122 fun visibility_for(m: MModule): MVisibility
123 do
124 if m == self then return intrude_visibility
125 if self.intrude_mmodules.has(m) then return intrude_visibility
126 if self.public_mmodules.has(m) then return public_visibility
127 if self.private_mmodules.has(m) then return private_visibility
128 return none_visibility
129 end
130
131 # Set the visibility of an imported module
132 # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
133 fun set_visibility_for(m: MModule, v: MVisibility)
134 do
135 if v == intrude_visibility then
136 self.intrude_mmodules.add(m)
137 self.intrude_mmodules.add_all(m.intrude_mmodules)
138 self.public_mmodules.add_all(m.public_mmodules)
139 self.private_mmodules.add_all(m.private_mmodules)
140 else if v == public_visibility then
141 self.public_mmodules.add(m)
142 self.public_mmodules.add_all(m.intrude_mmodules)
143 self.public_mmodules.add_all(m.public_mmodules)
144 else if v == private_visibility then
145 self.private_mmodules.add(m)
146 self.private_mmodules.add_all(m.intrude_mmodules)
147 self.private_mmodules.add_all(m.public_mmodules)
148 else
149 print "{self} visibility for {m} = {v}"
150 abort # invalid visibility
151 end
152 end
153
154 # The first module in the nesting hierarchy to export self as public
155 # This function is used to determine the canonical name of modules, classes and properties.
156 # REQUIRE: the visibility of all nesting modules is already set.
157 fun public_owner: nullable MModule
158 do
159 var res = self.direct_owner
160 var last = res
161 while last != null do
162 if last.visibility_for(self) >= public_visibility then res = last
163 last = last.direct_owner
164 end
165 return res
166 end
167
168 # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
169 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
170 do
171 var v = visibility_for(intro_mmodule)
172 if v == intrude_visibility then
173 return visibility >= private_visibility
174 else if v == public_visibility then
175 return visibility > private_visibility
176 else if v == private_visibility then
177 return visibility > private_visibility
178 else if v == none_visibility then
179 return false
180 else
181 abort
182 end
183 end
184 end
185
186 # A visibility (for modules, class and properties)
187 # Valid visibility are:
188 #
189 # * `intrude_visibility`
190 # * `public_visibility`
191 # * `protected_visibility`
192 # * `none_visibility`
193 #
194 # Note this class is basically an enum.
195 # FIXME: use a real enum once user-defined enums are available
196 class MVisibility
197 super Comparable
198 redef type OTHER: MVisibility
199
200 redef var to_s: String
201
202 private var level: Int
203
204 private init(s: String, level: Int)
205 do
206 self.to_s = s
207 self.level = level
208 end
209
210 # Is self give less visibility than other
211 # none < private < protected < public < intrude
212 redef fun <(other)
213 do
214 return self.level < other.level
215 end
216 end
217
218 fun intrude_visibility: MVisibility do return once new MVisibility("intrude", 4)
219 fun public_visibility: MVisibility do return once new MVisibility("public", 4)
220 fun protected_visibility: MVisibility do return once new MVisibility("protected", 3)
221 fun private_visibility: MVisibility do return once new MVisibility("private", 2)
222 fun none_visibility: MVisibility do return once new MVisibility("none", 2)