Merge: Fix warnings in the JSON, SAX and SAXophoNit libraries and in the `neo_doxygen...
[nit.git] / lib / sax / helpers / attributes_impl.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
9 # another product.
10
11 # Default implementation of the Attributes interface.
12 module sax::attributes_impl
13
14 import sax::attributes
15
16 # Default implementation of the Attributes interface.
17 #
18 # This class provides a default implementation of the SAX2
19 # `Attributes` interface, with the addition of manipulators so that the list
20 # can be modified or reused.
21 #
22 # There are two typical uses of this class:
23 #
24 # * to take a persistent snapshot of an Attributes object in a `start_element`
25 # event; or
26 # * to construct or modify an Attributes object in a SAX2 driver or filter.
27 #
28 # Note: The original source code and documentation of this class comes, in part,
29 # from [SAX 2.0](http://www.saxproject.org).
30 class AttributesImpl
31 super Attributes
32
33 private var data = new Array[String]
34 redef var length: Int = 0
35
36 redef fun uri(index: Int): nullable String do
37 if index >= 0 and index < length then
38 return data[index * 5]
39 else
40 return null
41 end
42 end
43
44 redef fun local_name(index: Int): nullable String do
45 if index >= 0 and index < length then
46 return data[index * 5 + 1]
47 else
48 return null
49 end
50 end
51
52 redef fun qname(index: Int): nullable String do
53 if index >= 0 and index < length then
54 return data[index * 5 + 2]
55 else
56 return null
57 end
58 end
59
60 # Look up an attribute's type by index.
61 #
62 # The attribute type is one of the strings `CDATA`, `ID`,
63 # `IDREF`, `IDREFS`, `NMTOKEN`, `NMTOKENS`, `ENTITY`, `ENTITIES`,
64 # or `NOTATION` (always in upper case).
65 #
66 # If the parser has not read a declaration for the attribute,
67 # or if the parser does not report attribute types, then it must
68 # return the value `CDATA` as stated in the XML 1.0 Recommentation
69 # (clause 3.3.3, "Attribute-Value Normalization").
70 #
71 # For an enumerated attribute that is not a notation, the
72 # parser will report the type as `NMTOKEN`.
73 #
74 # Parameters:
75 #
76 # * `index: Int`: attribute index.
77 # * `index: String`: XML 1.0 qualified (prefixed) name.
78 # In many cases, it will be more efficient to look up the name once and
79 # query by `Int` index rather than quering by name repeatedly.
80 #
81 # Returns:
82 #
83 # The attribute's type as a string, or `null` if the specified
84 # attribute is not in the list or if qualified names
85 # are not available.
86 #
87 # SEE: `length`
88 redef fun type_of(index): nullable String do
89 if index isa Int then
90 if index >= 0 and index < length then
91 return data[index * 5 + 3]
92 end
93 else if index isa String and "" != index then
94 var i: Int = 0
95
96 while i < data.length do
97 if data[i + 2] == index then
98 return data[i + 3]
99 end
100 i += 5
101 end
102 end
103 return null
104 end
105
106 # Look up an attribute's value by index.
107 #
108 # If the attribute value is a list of tokens (`IDREFS`,
109 # `ENTITIES`, or `NMTOKENS`), the tokens will be concatenated
110 # into a single string with each token separated by a
111 # single space.
112 #
113 # Parameters:
114 #
115 # * `index: Int`: attribute index.
116 # * `index: String`: XML 1.0 qualified (prefixed) name.
117 # In many cases, it will be more efficient to look up the name once and
118 # query by `Int` index rather than quering by name repeatedly.
119 #
120 # Returns:
121 #
122 # The attribute's value as a string, or `null` if the specified
123 # attribute is not in the list or if qualified names
124 # are not available.
125 #
126 # SEE: `length`
127 redef fun value_of(index): nullable String do
128 if index isa Int then
129 if index >= 0 and index < length then
130 return data[index * 5 + 4]
131 end
132 else if index isa String and "" != index then
133 var i: Int = 0
134
135 while i < data.length do
136 if data[i + 2] == index then
137 return data[i + 4]
138 end
139 i += 5
140 end
141 end
142 return null
143 end
144
145 # Look up the index of an attribute by Namespace name.
146 #
147 # In many cases, it will be more efficient to look up the name once and
148 # query by `Int` index rather than quering by name repeatedly.
149 #
150 # Parameters:
151 #
152 # * `uri`: Namespace URI, or the empty string if
153 # the name has no Namespace URI.
154 # * `local_name`: attribute's local name.
155 #
156 # Returns:
157 #
158 # The index of the attribute, or -1 if it does not
159 # appear in the list.
160 redef fun index_ns(uri: String, local_name: String): Int do
161 var i: Int = 0
162
163 if "" != local_name then
164 while i < data.length do
165 if data[i] == uri and data[i + 1] == local_name then
166 return i / 5
167 end
168 i += 5
169 end
170 end
171 return -1
172 end
173
174 # Look up the index of an attribute by XML 1.0 qualified name.
175 #
176 # In many cases, it will be more efficient to look up the name once and
177 # query by `Int` index rather than quering by name repeatedly.
178 #
179 # Parameters:
180 #
181 # * `qname`: XML 1.0 qualified (prefixed) name.
182 #
183 # Returns:
184 #
185 # The index of the attribute, or -1 if it does not
186 # appear in the list.
187 redef fun index_of(qname: String): Int do
188 var i: Int = 0
189
190 if "" != qname then
191 while i < data.length do
192 if data[i + 2] == qname then
193 return i / 5
194 end
195 i += 5
196 end
197 end
198 return -1
199 end
200
201 # Look up an attribute's type by Namespace name.
202 #
203 # In many cases, it will be more efficient to look up the name once and
204 # query by `Int` index rather than quering by name repeatedly.
205 #
206 # See `type_of` for a description
207 # of the possible types.
208 #
209 # Parameters:
210 #
211 # * `uri`: Namespace URI, or the empty string if
212 # the name has no Namespace URI.
213 #
214 # * `local_name`: attribute's local name.
215 #
216 # Returns:
217 #
218 # The attribute type as a string, or `null` if the
219 # attribute is not in the list or if Namespace
220 # processing is not being performed.
221 redef fun type_ns(uri: String, local_name: String): nullable String do
222 var i: Int = 0
223
224 if "" != local_name then
225 while i < data.length do
226 if data[i] == uri and data[i + 1] == local_name then
227 return data[i + 3]
228 end
229 i += 5
230 end
231 end
232 return null
233 end
234
235 # Look up an attribute's value by Namespace name.
236 #
237 # In many cases, it will be more efficient to look up the name once and
238 # query by `Int` index rather than quering by name repeatedly.
239 #
240 # See `value_of` for a description
241 # of the possible values.
242 #
243 # Parameters:
244 #
245 # * `uri`: Namespace URI, or the empty string if
246 # the name has no Namespace URI.
247 #
248 # * `local_name`: attribute's local name.
249 #
250 # Returns:
251 #
252 # The attribute value as a string, or `null` if the
253 # attribute is not in the list or if Namespace
254 # processing is not being performed.
255 redef fun value_ns(uri: String, local_name: String): nullable String do
256 var i: Int = 0
257
258 if "" != local_name then
259 while i < data.length do
260 if data[i] == uri and data[i + 1] == local_name then
261 return data[i + 4]
262 end
263 i += 5
264 end
265 end
266 return null
267 end
268
269 # Clear the attribute list for reuse.
270 fun clear do
271 data.clear
272 length = 0
273 end
274
275 # Copy an entire Attributes object.
276 #
277 # It may be more efficient to reuse an existing object
278 # rather than constantly allocating new ones.
279 #
280 # Parameters:
281 #
282 # * `atts`: attributes to copy.
283 fun attributes=(atts: Attributes) do
284 var i: Int = 0
285
286 clear
287 length = atts.length
288 data.enlarge(length * 5)
289 while i < length do
290 data.push(atts.uri(i).as(not null))
291 data.push(atts.local_name(i).as(not null))
292 data.push(atts.qname(i).as(not null))
293 data.push(atts.type_of(i).as(not null))
294 data.push(atts.value_of(i).as(not null))
295 i += 1
296 end
297 end
298
299 # Add an attribute to the end of the list.
300 #
301 # For the sake of speed, this method does no checking
302 # to see if the attribute is already in the list: that is
303 # the responsibility of the application.
304 #
305 # Parameters:
306 #
307 # * `uri`: Namespace URI, or the empty string if
308 # none is available or Namespace processing is not being performed.
309 # * `local_name`: local name, or the empty string if
310 # Namespace processing is not being performed.
311 # * `qname`: qualified (prefixed) name, or the empty string
312 # if qualified names are not available.
313 # * `attribute_type`: attribute type as a string.
314 # * `value`: attribute value.
315 fun add(uri: String, local_name: String, qname: String,
316 attribute_type: String, value: String) do
317 ensure_capacity(length + 1)
318 data.push(uri)
319 data.push(local_name)
320 data.push(qname)
321 data.push(attribute_type)
322 data.push(value)
323 length += 1
324 end
325
326 # Set an attribute in the list.
327 #
328 # For the sake of speed, this method does no checking
329 # for name conflicts or well-formedness: such checks are the
330 # responsibility of the application.
331 #
332 # Parameters:
333 #
334 # * `index`: index of the attribute (zero-based).
335 # * `uri`: Namespace URI, or the empty string if
336 # none is available or Namespace processing is not being performed.
337 # * `local_name`: local name, or the empty string if
338 # Namespace processing is not being performed.
339 # * `qname`: qualified (prefixed) name, or the empty string
340 # if qualified names are not available.
341 # * `attribute_type`: attribute type as a string.
342 # * `value`: attribute value.
343 fun set(index: Int, uri: String, local_name: String, qname: String,
344 attribute_type: String, value: String) do
345 assert index_in_bounds: index >= 0 and index < length
346 data[index * 5] = uri
347 data[index * 5 + 1] = local_name
348 data[index * 5 + 2] = qname
349 data[index * 5 + 3] = attribute_type
350 data[index * 5 + 4] = value
351 end
352
353 # Remove an attribute from the list.
354 #
355 # Parameters:
356 #
357 # * `index`: index of the attribute (zero-based).
358 fun remove_at(index: Int) do
359 assert index_in_bounds: index >= 0 and index < length
360 index = index * 5
361 for i in [1..5] do
362 data.remove_at(index)
363 end
364 length -= 1
365 end
366
367 # Set the Namespace URI of a specific attribute.
368 #
369 # Parameters:
370 #
371 # * `index`: index of the attribute (zero-based).
372 # * `uri`: attribute's Namespace URI, or the empty string for none.
373 fun uri=(index: Int, uri: String) do
374 assert index_in_bounds: index >= 0 and index < length
375 data[index * 5] = uri
376 end
377
378 # Set the local name of a specific attribute.
379 #
380 # Parameters:
381 #
382 # * `index`: index of the attribute (zero-based).
383 # * `local_name`: attribute's local name, or the empty string for none.
384 fun local_name=(index: Int, local_name: String) do
385 assert index_in_bounds: index >= 0 and index < length
386 data[index * 5 + 1] = local_name
387 end
388
389 # Set the qualified name of a specific attribute.
390 #
391 # Parameters:
392 #
393 # * `index`: index of the attribute (zero-based).
394 # * `qname`: attribute's qualified name, or the empty string for none.
395 fun qname=(index: Int, qname: String) do
396 assert index_in_bounds: index >= 0 and index < length
397 data[index * 5 + 2] = qname
398 end
399
400 # Set the type of a specific attribute.
401 #
402 # Parameters:
403 #
404 # * `index`: index of the attribute (zero-based).
405 # * `attribute_type`: attribute's type.
406 fun type_of=(index: Int, attribute_type: String) do
407 assert index_in_bounds: index >= 0 and index < length
408 data[index * 5 + 3] = attribute_type
409 end
410
411 # Set the value of a specific attribute.
412 #
413 # Parameters:
414 #
415 # * `index`: index of the attribute (zero-based).
416 # * `value`: attribute's value.
417 fun value_of=(index: Int, value: String) do
418 assert index_in_bounds: index >= 0 and index < length
419 data[index * 5 + 4] = value
420 end
421
422 # Ensure the internal array's capacity.
423 #
424 # Parameters:
425 #
426 # * `n`: minimum number of attributes that the array must be able to hold.
427 private fun ensure_capacity(n: Int) do
428 data.enlarge(n * 5)
429 end
430 end