301fc23336db2ffff3e685a83b9ba439215705f4
[nit.git] / contrib / neo_doxygen / src / model / module_compound.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Nodes for modules and files.
16 module model::module_compound
17
18 import graph
19 import class_compound
20 import namespace_members
21
22 # A source file.
23 #
24 # Creates one modules by inner namespace. The full name of the modules begin
25 # with the namespace’s full name, and end with the unqualified name of the file,
26 # without the extension.
27 #
28 # Note: If a module associated to the root namespace is needed, it is added to
29 # the graph only when `put_edges` is called.
30 class FileCompound
31 super Compound
32 super CodeBlock
33
34 # Modules corresponding to the namespaces defined/redefined in the file.
35 private var inner_namespaces = new Array[Module]
36
37 # `model_id` of the classes declared in the file.
38 private var inner_classes = new Array[String]
39
40 # The last component of the path, without the extension.
41 #
42 # Used as the unqualified name of the modules.
43 private var basename: String = ""
44
45 init do
46 super
47 end
48
49 redef fun name_separator: String do return "/"
50
51 redef fun location=(location: nullable Location) do
52 super
53 if location != null and location.path != null then
54 full_name = location.path.as(not null)
55 end
56 for m in inner_namespaces do m.location = location
57 end
58
59 redef fun name=(name: String) do
60 # Example: `MyClass.java`
61 super
62 var match = name.search_last(".")
63
64 if match == null then
65 basename = name
66 else
67 basename = name.substring(0, match.from)
68 end
69 # Update the modules’ name.
70 for m in inner_namespaces do m.update_name
71 end
72
73 redef fun declare_namespace(id: String, full_name: String) do
74 var m: Module
75
76 assert not full_name.is_empty or id.is_empty else
77 sys.stderr.write "Inner mamespace declarations without name are not yet supported (except for the root namespace).\n"
78 end
79 m = new Module(graph, self, new NamespaceRef(id, full_name))
80 m.location = self["location"].as(nullable Location)
81 inner_namespaces.add m
82 end
83
84 redef fun declare_class(id, full_name, prot) do
85 assert not id.is_empty else
86 sys.stderr.write "Inner class declarations without ID are not yet supported.\n"
87 end
88 inner_classes.add id
89 end
90
91 redef fun put_in_graph do
92 # Do not add `self` to the Neo4j graph...
93 # ... but add its modules...
94 for m in inner_namespaces do m.put_in_graph
95 # ... and add `self` to the indexes.
96 if model_id != "" then graph.by_id[model_id] = self
97 graph.files.add self
98 end
99
100 # If the file contains some classes in the root namespace, add an implicit
101 # module to handle them.
102 #
103 # This method is called by `ProjectGraph.add_global_modules` and assumes
104 # that all the namespaces are already fully set and put in the graph.
105 fun declare_root_namespace do
106 if has_globals then
107 declare_namespace("", "")
108 inner_namespaces.last.put_in_graph
109 end
110 end
111
112 # Does this file contain classes in the root namespace?
113 private fun has_globals: Bool do
114 var root = graph.by_id[""]
115 for c in inner_classes do
116 if graph.class_to_ns[c] == root then return true
117 end
118 return false
119 end
120 end
121
122 # A `MModule` node.
123 #
124 # For each file, there is one module by inner namespace.
125 private class Module
126 super Compound
127 super CodeBlock
128
129 # The file that declares the module.
130 var file_compound: FileCompound
131
132 # The namespace defined or redefined by the module.
133 var namespace: NamespaceRef
134
135 init do
136 super
137 self.labels.add("MModule")
138 update_name
139 end
140
141 # Update the `full_name` and the `name`.
142 #
143 # Update the short name of the module to the `basename` of the file that
144 # declares it.
145 fun update_name do
146 name = file_compound.basename
147 parent_name = namespace.full_name
148 end
149
150 redef fun put_in_graph do
151 super
152 end
153
154 redef fun put_edges do
155 var ns_compound = namespace.seek_in(graph)
156 var self_class = ns_compound.self_class
157
158 graph.add_edge(ns_compound, "DECLARES", self)
159
160 for c in file_compound.inner_classes do
161 if graph.class_to_ns[c] != ns_compound then continue
162 var class_compound = graph.by_id[c].as(ClassCompound)
163 graph.add_edge(self, "INTRODUCES", class_compound)
164 graph.add_edge(self, "DEFINES", class_compound.class_def)
165 end
166
167 if self_class isa SelfClass then
168 # We assume that only one file is linked to the namespace.
169 # TODO When Doxygen will provide a way to know which file defines which member, use it.
170 self_class.location = file_compound.location
171 graph.add_edge(self, "INTRODUCES", self_class)
172 graph.add_edge(self, "DEFINES", self_class.class_def)
173 end
174 end
175 end
176
177 # Adds the `add_global_modules` phase to `ProjectGraph`.
178 redef class ProjectGraph
179
180 # Project’s source files.
181 var files: SimpleCollection[FileCompound] = new Array[FileCompound]
182
183 # Add the modules that define the root namespace.
184 #
185 # **Must** be called before any call to `put_edges`, and after all the
186 # namespaces are fully set and put in the graph.
187 #
188 # Note: This method is not idempotent so it has to be called only once.
189 fun add_global_modules do
190 for f in files do f.declare_root_namespace
191 end
192 end