neo_doxygen: Add missing documentation.
[nit.git] / contrib / neo_doxygen / src / doxml / listener.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 # Basic SAX listeners.
16 module doxml::listener
17
18 import saxophonit
19 import model
20 import language_specific
21
22 # Common abstractions for SAX listeners reading XML documents generated by Doxygen.
23 abstract class DoxmlListener
24 super ContentHandler
25
26 # The locator setted by calling `document_locator=`.
27 protected var locator: nullable SAXLocator = null
28
29 # The project graph.
30 fun graph: ProjectGraph is abstract
31
32 # The language-specific strategies to use.
33 fun source_language: SourceLanguage is abstract
34
35 redef fun document_locator=(locator: SAXLocator) do
36 self.locator = locator
37 end
38
39 # The Doxygen’s namespace IRI.
40 protected fun dox_uri: String do return ""
41
42 redef fun start_element(uri: String, local_name: String, qname: String,
43 atts: Attributes) do
44 super
45 if uri != dox_uri then return # None of our business.
46 start_dox_element(local_name, atts)
47 end
48
49 # Process the start of an element in the Doxygen’s namespace.
50 #
51 # See `ContentHandler.start_element` for the description of the parameters.
52 protected fun start_dox_element(local_name: String, atts: Attributes) do end
53
54 redef fun end_element(uri: String, local_name: String, qname: String) do
55 super
56 if uri != dox_uri then return # None of our business.
57 end_dox_element(local_name)
58 end
59
60 # Process the end of an element in the Doxygen’s namespace.
61 #
62 # See `ContentHandler.start_element` for the description of the parameters.
63 protected fun end_dox_element(local_name: String) do end
64
65 # Get the boolean value of the specified attribute.
66 #
67 # `false` by default.
68 protected fun get_bool(atts: Attributes, local_name: String): Bool do
69 return get_optional(atts, local_name, "no") == "yes"
70 end
71
72 # Get the value of an optional attribute.
73 #
74 # Parameters:
75 #
76 # * `atts`: attribute list.
77 # * `local_name`: local name of the attribute.
78 # * `default`: value to return when the specified attribute is not found.
79 protected fun get_optional(atts: Attributes, local_name: String,
80 default: String): String do
81 return atts.value_ns(dox_uri, local_name) or else default
82 end
83
84 # Get the value of an required attribute.
85 #
86 # Parameters:
87 #
88 # * `atts`: attribute list.
89 # * `local_name`: local name of the attribute.
90 protected fun get_required(atts: Attributes, local_name: String): String do
91 var value = atts.value_ns(dox_uri, local_name)
92 if value == null then
93 throw_error("The `{local_name}` attribute is required.")
94 return ""
95 else
96 return value
97 end
98 end
99
100 redef fun end_document do
101 locator = null
102 end
103
104 # Throw an error with the specified message by prepending the current location.
105 protected fun throw_error(message: String) do
106 var e: SAXParseException
107
108 if locator != null then
109 e = new SAXParseException.with_locator(message, locator.as(not null))
110 else
111 e = new SAXParseException(message)
112 end
113 e.throw
114 end
115 end
116
117 # A `DoxmlListener` that read only a part of a document.
118 #
119 # Temporary redirect events to itself until it ends processing its part.
120 abstract class StackableListener
121 super DoxmlListener
122
123 # The associated reader.
124 var reader: XMLReader
125
126 # The parent listener.
127 var parent: DoxmlListener
128
129 # Namespace’s IRI of the element at the root of the part to process.
130 private var root_uri: String = ""
131
132 # Local name of the element at the root of the part to process.
133 private var root_local_name: String = ""
134
135 # The number of open element of the same type than the root of the part to process.
136 private var depth = 0
137
138 # The project graph.
139 private var p_graph: ProjectGraph is noinit
140
141 # The language-specific strategies to use.
142 private var p_source: SourceLanguage is noinit
143
144
145 init do
146 super
147 p_graph = parent.graph
148 p_source = parent.source_language
149 end
150
151 redef fun graph do return p_graph
152 redef fun source_language do return p_source
153
154 # Temporary redirect events to itself until the end of the specified element.
155 fun listen_until(uri: String, local_name: String) do
156 root_uri = uri
157 root_local_name = local_name
158 depth = 1
159 reader.content_handler = self
160 locator = parent.locator
161 end
162
163 redef fun start_element(uri: String, local_name: String, qname: String,
164 atts: Attributes) do
165 super
166 if uri == root_uri and local_name == root_local_name then
167 depth += 1
168 end
169 end
170
171 redef fun end_element(uri: String, local_name: String, qname: String) do
172 super
173 if uri == root_uri and local_name == root_local_name then
174 depth -= 1
175 if depth <= 0 then
176 end_listening
177 parent.end_element(uri, local_name, qname)
178 end
179 end
180 end
181
182 # Reset the reader’s listener to the parent.
183 fun end_listening do
184 reader.content_handler = parent
185 locator = null
186 end
187
188 redef fun end_document do
189 end_listening
190 end
191 end
192
193 # A SAX listener that skips any event except the end of the part to process.
194 #
195 # Used to skip an entire element.
196 class NoopListener
197 super StackableListener
198 end
199
200 # Concatenates any text node found.
201 class TextListener
202 super StackableListener
203
204 # The read text.
205 protected var buffer: Buffer = new FlatBuffer
206
207 # Is the last read chunk was ignorable white space?
208 private var sp: Bool = false
209
210 redef fun listen_until(uri: String, local_name: String) do
211 buffer.clear
212 sp = false
213 super
214 end
215
216 redef fun characters(str: String) do
217 if sp then
218 if buffer.length > 0 then buffer.append(" ")
219 sp = false
220 end
221 buffer.append(str)
222 end
223
224 redef fun ignorable_whitespace(str: String) do
225 sp = true
226 end
227
228 # Flush the buffer.
229 protected fun flush_buffer: String do
230 var s = buffer.to_s
231
232 buffer.clear
233 sp = false
234 return s
235 end
236
237 redef fun to_s do return buffer.to_s
238 end
239
240 # Processes a content of type `linkedTextType`.
241 abstract class LinkedTextListener[T: LinkedText]
242 super TextListener
243
244 # The read text.
245 var linked_text: T is noinit
246
247 private var refid = ""
248
249 # Create a new instance of `T`.
250 protected fun create_linked_text: T is abstract
251
252 redef fun listen_until(uri: String, local_name: String) do
253 linked_text = create_linked_text
254 refid = ""
255 super
256 end
257
258 redef fun start_dox_element(local_name: String, atts: Attributes) do
259 super
260 push_part
261 if "ref" == local_name then refid = get_required(atts, "refid")
262 end
263
264 redef fun end_dox_element(local_name: String) do
265 super
266 push_part
267 if "ref" == local_name then refid = ""
268 end
269
270 private fun push_part do
271 var s = flush_buffer
272
273 if not s.is_empty then
274 linked_text.add_part(s, refid)
275 end
276 end
277
278 redef fun to_s do return linked_text.to_s
279 end
280
281 # Processes the content of a `<type>` element.
282 class TypeListener
283 super LinkedTextListener[RawType]
284
285 private var raw_type: RawType is noinit
286
287 redef fun create_linked_text do return new RawType(graph)
288 end