404bcacb031cf1963eb7565faef2dd10ac91bb70
[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
21 # A source file.
22 #
23 # Creates one modules by inner namespace. The full name of the modules begin
24 # with the namespace’s full name, and end with the unqualified name of the file,
25 # without the extension.
26 #
27 # Note: If a module associated to the root namespace is needed, it is added to
28 # the graph only when `put_edges` is called.
29 class FileCompound
30 super Compound
31 super CodeBlock
32
33 # Modules corresponding to the namespaces defined/redefined in the file.
34 private var inner_namespaces = new Array[Module]
35
36 # `model_id` of the classes declared in the file.
37 private var inner_classes = new Array[String]
38
39 # The last component of the path, without the extension.
40 #
41 # Used as the unqualified name of the modules.
42 private var basename: String = ""
43
44 init do
45 super
46 end
47
48 redef fun name_separator: String do return "/"
49
50 redef fun location=(location: nullable Location) do
51 super
52 if location != null and location.path != null then
53 full_name = location.path.as(not null)
54 end
55 for m in inner_namespaces do m.location = location
56 end
57
58 redef fun name=(name: String) do
59 # Example: `MyClass.java`
60 super
61 var match = name.search_last(".")
62
63 if match == null then
64 basename = name
65 else
66 basename = name.substring(0, match.from)
67 end
68 # Update the modules’ name.
69 for m in inner_namespaces do m.update_name
70 end
71
72 redef fun declare_namespace(id: String, full_name: String) do
73 var m: Module
74
75 assert not full_name.is_empty or id.is_empty else
76 sys.stderr.write "Inner mamespace declarations without name are not yet supported (except for the root namespace).\n"
77 end
78 m = new Module(graph, self, new NamespaceRef(id, full_name))
79 m.location = self["location"].as(nullable Location)
80 inner_namespaces.add m
81 end
82
83 redef fun declare_class(id, full_name, prot) do
84 assert not id.is_empty else
85 sys.stderr.write "Inner class declarations without ID are not yet supported.\n"
86 end
87 inner_classes.add id
88 end
89
90 redef fun put_in_graph do
91 # Do not add `self` to the Neo4j graph...
92 # ... but add its modules...
93 for m in inner_namespaces do m.put_in_graph
94 # ... and add `self` to the indexes.
95 if model_id != "" then graph.by_id[model_id] = self
96 graph.files.add self
97 end
98
99 # If the file contains some classes in the root namespace, add an implicit
100 # module to handle them.
101 #
102 # This method is called by `ProjectGraph.add_global_modules` and assumes
103 # that all the namespaces are already fully set and put in the graph.
104 fun declare_root_namespace do
105 if has_globals then
106 declare_namespace("", "")
107 inner_namespaces.last.put_in_graph
108 end
109 end
110
111 # Does this file contain classes in the root namespace?
112 private fun has_globals: Bool do
113 var root = graph.by_id[""]
114 for c in inner_classes do
115 if graph.class_to_ns[c] == root then return true
116 end
117 return false
118 end
119 end
120
121 # A `MModule` node.
122 #
123 # For each file, there is one module by inner namespace.
124 private class Module
125 super Compound
126 super CodeBlock
127
128 # The file that declares the module.
129 var file_compound: FileCompound
130
131 # The namespace defined or redefined by the module.
132 var namespace: NamespaceRef
133
134 init do
135 super
136 self.labels.add("MModule")
137 update_name
138 end
139
140 # Update the `full_name` and the `name`.
141 #
142 # Update the short name of the module to the `basename` of the file that
143 # declares it.
144 fun update_name do
145 name = file_compound.basename
146 parent_name = namespace.full_name
147 end
148
149 redef fun put_edges do
150 var ns_compound = namespace.seek_in(graph)
151 graph.add_edge(ns_compound, "DECLARES", self)
152
153 for c in file_compound.inner_classes do
154 if graph.class_to_ns[c] != ns_compound then continue
155 var class_compound = graph.by_id[c].as(ClassCompound)
156 graph.add_edge(self, "INTRODUCES", class_compound)
157 graph.add_edge(self, "DEFINES", class_compound.class_def)
158 end
159 end
160 end
161
162 # Adds the `add_global_modules` phase to `ProjectGraph`.
163 redef class ProjectGraph
164
165 # Project’s source files.
166 var files: SimpleCollection[FileCompound] = new Array[FileCompound]
167
168 # Add the modules that define the root namespace.
169 #
170 # **Must** be called before any call to `put_edges`, and after all the
171 # namespaces are fully set and put in the graph.
172 #
173 # Note: This method is not idempotent so it has to be called only once.
174 fun add_global_modules do
175 for f in files do f.declare_root_namespace
176 end
177 end