PageRenderTime 7ms CodeModel.GetById 2ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/xml/namespaces/xmlns_parser.e

http://github.com/tybor/Liberty
Specman e | 462 lines | 387 code | 40 blank | 35 comment | 38 complexity | dc104d87507c9bcae1eaa70486679035 MD5 | raw file
  1-- See the Copyright notice at the end of this file.
  2--
  3class XMLNS_PARSER
  4   --
  5   -- A namespace-aware XML parser
  6   --
  7
  8inherit
  9   XML_CALLBACKS
 10      rename
 11         validator as xml_validator
 12         set_validator as set_xml_validator
 13      end
 14
 15insert
 16   XML_NAMESPACES
 17
 18create {ANY}
 19   connect_to, make
 20
 21feature {ANY}
 22   parse (a_callbacks: XMLNS_CALLBACKS)
 23         -- Parse an XML documents by sending parsing events to the given `callbacks'
 24      require
 25         is_connected
 26      do
 27         callbacks := a_callbacks
 28         parser.parse(Current)
 29         if validator /= Void then
 30            validator.the_end
 31         end
 32      end
 33
 34   connect_to (a_url: URL)
 35         -- Connect to the given XML document
 36      require
 37         not is_connected
 38         a_url.is_connected implies a_url.read
 39      do
 40         make
 41         parser.connect_to(a_url)
 42      end
 43
 44   disconnect
 45      require
 46         is_connected
 47      do
 48         parser.disconnect
 49      ensure
 50         not is_connected
 51      end
 52
 53   is_connected: BOOLEAN
 54      do
 55         Result := parser.is_connected
 56      end
 57
 58   line: INTEGER
 59      do
 60         Result := parser.line
 61      end
 62
 63   column: INTEGER
 64      do
 65         Result := parser.column
 66      end
 67
 68feature {}
 69   make
 70         -- Create a not connected parser
 71      do
 72         if parser = Void then -- this test is useful when called from `connect_to'
 73            create parser.make
 74            create namespaces.make(0)
 75         else
 76            from
 77            until
 78               namespaces.is_empty
 79            loop
 80               old_namespaces(namespaces.last)
 81               namespaces.remove_last
 82            end
 83         end
 84         attributes_for_new_node := False
 85      end
 86
 87   parser: XML_PARSER
 88
 89   callbacks: XMLNS_CALLBACKS
 90
 91feature {XML_NAMESPACES}
 92   set_validator (a_validator: XMLNS_VALIDATOR)
 93      do
 94         callbacks.set_validator(a_validator)
 95      end
 96
 97   validator: XMLNS_VALIDATOR
 98      do
 99         Result := callbacks.validator
100      end
101
102feature {XML_PARSER}
103   with_attribute (attribute_name: UNICODE_STRING; attribute_value: UNICODE_STRING; l, c: INTEGER)
104      do
105         if not attributes_for_new_node then
106            namespaces.add_last(new_namespaces)
107            attributes_for_new_node := True
108         end
109         if attribute_name.has_prefix(once U"xml") then
110            xml_attribute(attribute_name, attribute_value, l, c)
111         else
112            split_namespace(attribute_name, l, c)
113            if not at_error then
114               if validator /= Void then
115                  validator.with_attribute(namespace, name, attribute_value, l, c)
116               end
117               callbacks.with_attribute(namespace, name, attribute_value, l, c)
118            end
119         end
120      end
121
122   open_node (node_name: UNICODE_STRING; l, c: INTEGER)
123      do
124         if attributes_for_new_node then
125            attributes_for_new_node := False
126         else
127            namespaces.add_last(new_namespaces)
128         end
129         split_namespace(node_name, l, c)
130         if not at_error then
131            if validator = Void then
132               callbacks.open_node(namespace, name, l, c)
133            elseif validator.is_valid_open_node(namespace, name, l, c) then
134               validator.open_node(namespace, name, l, c)
135               callbacks.open_node(namespace, name, l, c)
136            else
137               parse_error(l, c, once "Invalid opening tag")
138            end
139         end
140      end
141
142   close_node (node_name: UNICODE_STRING; l, c: INTEGER)
143      do
144         split_namespace(node_name, l, c)
145         if not at_error then
146            if validator = Void then
147               callbacks.close_node(namespace, name, l, c)
148            elseif validator.is_valid_close_node(namespace, name, l, c) then
149               validator.close_node(namespace, name, l, c)
150               callbacks.close_node(namespace, name, l, c)
151            else
152               parse_error(l, c, once "Invalid closing tag")
153            end
154            old_namespaces(namespaces.last)
155            namespaces.remove_last
156         end
157      end
158
159   open_close_node (node_name: UNICODE_STRING; l, c: INTEGER)
160      local
161         local_namespaces: BOOLEAN
162      do
163         if attributes_for_new_node then
164            attributes_for_new_node := False
165            local_namespaces := True
166         end
167         split_namespace(node_name, l, c)
168         if not at_error then
169            if validator = Void then
170               callbacks.open_close_node(namespace, name, l, c)
171            elseif validator.is_valid_open_close_node(namespace, name, l, c) then
172               validator.open_close_node(namespace, name, l, c)
173               callbacks.open_close_node(namespace, name, l, c)
174            else
175               parse_error(l, c, once "Invalid empty tag")
176            end
177            if local_namespaces then
178               old_namespaces(namespaces.last)
179               namespaces.remove_last
180            end
181         end
182      end
183
184   xml_header (l, c: INTEGER)
185      do
186         callbacks.xml_header(l, c)
187      end
188
189   processing_instruction (a_target, a_data: UNICODE_STRING)
190      do
191         callbacks.processing_instruction(a_target, a_data)
192      end
193
194   entity (a_entity: UNICODE_STRING; l, c: INTEGER): UNICODE_STRING
195      do
196         if validator = Void then
197            Result := callbacks.entity(a_entity, l, c)
198         else
199            Result := validator.entity(a_entity, l, c)
200            if Result = Void then
201               Result := callbacks.entity(a_entity, l, c)
202            end
203         end
204      end
205
206   current_node: UNICODE_STRING
207      local
208         ns, cn: UNICODE_STRING
209      do
210         cn := callbacks.current_node
211         if cn /= Void then
212            Result := once U""
213            ns := callbacks.current_namespace
214            if ns = Void or else ns.is_empty then -- Void only when at error
215               Result.copy(cn)
216            else
217               Result.copy(find_namespace_ref(ns))
218               Result.extend(':'.code)
219               Result.append(cn)
220            end
221         end
222      end
223
224   data (a_data: UNICODE_STRING; l, c: INTEGER)
225      do
226         if validator = Void then
227            callbacks.data(a_data, l, c)
228         elseif validator.is_valid_data(a_data, l, c) then
229            validator.data(a_data, l, c)
230            callbacks.data(a_data, l, c)
231         else
232            parse_error(l, c, once "Invalid data")
233         end
234      end
235
236   parse_error (l, c: INTEGER; message: STRING)
237      do
238         callbacks.parse_error(l, c, message)
239      end
240
241   at_error: BOOLEAN
242      do
243         Result := callbacks.at_error
244      end
245
246feature {}
247   namespace: UNICODE_STRING
248         -- set by `split_namespace'
249
250   name: UNICODE_STRING
251         -- set by `split_namespace'
252
253   split_namespace (a_name: UNICODE_STRING; l, c: INTEGER)
254         -- Sets `namespace' and `name' according to the given name, splitting at the first colon (':'). If
255         -- there is no colon, `namespace' is Void and `name' contains the full given name. Otherwise
256         -- `namespace' contains the URI of the namespace and `name' contains the part of the given name after
257         -- the first colon.
258      local
259         i: INTEGER; ns: UNICODE_STRING; error: STRING
260      do
261         name := once U""
262         i := a_name.first_index_of(':'.code)
263         if a_name.valid_index(i) then
264            ns := once U""
265            ns.copy_substring(a_name, 1, i - 1)
266            name.copy_substring(a_name, i + 1, a_name.upper)
267            namespace := find_namespace(ns)
268            if namespace = Void then
269               error := once ""
270               error.copy(once "Unknown namespace prefix: ")
271               ns.utf8_encode_in(error)
272               parse_error(l, c, error)
273            end
274         else
275            name.copy(a_name)
276            namespace := Void
277         end
278      end
279
280   namespaces: FAST_ARRAY[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]
281         -- The known namespaces
282
283   xml_attribute (attribute_name, attribute_value: UNICODE_STRING; l, c: INTEGER)
284      require
285         attribute_name /= Void
286      local
287         old_value, ns: UNICODE_STRING; error: STRING
288         action: PROCEDURE[TUPLE[XMLNS_PARSER, UNICODE_STRING]]
289      do
290         if attribute_name.is_equal(once U"xmlns") then
291            ns := once U""
292            check
293               ns.is_empty
294            end
295         elseif attribute_name.has_prefix(once U"xmlns:") then
296            ns := once U""
297            ns.copy_substring(attribute_name, 7, attribute_name.upper)
298         end
299         if ns /= Void then
300            action := namespace_actions.reference_at(attribute_value)
301            if action /= Void then
302               action.call([Current, ns])
303            end
304            old_value := namespaces.last.reference_at(ns)
305            if old_value /= Void then
306               string_recycle(namespaces.last.key_at(old_value))
307               string_recycle(old_value)
308               namespaces.last.remove(ns)
309            end
310            namespaces.last.add(string_twin(attribute_value), string_twin(ns))
311            check
312               find_namespace(ns).is_equal(attribute_value)
313            end
314         else
315            error := once ""
316            error.copy(once "Bad namespace prefix (must not begin by %"xml%") for attribute: %"")
317            attribute_name.utf8_encode_in(error)
318            error.extend('"')
319            parse_error(l, c, error)
320         end
321      end
322
323   find_namespace (a_namespace_ref: UNICODE_STRING): UNICODE_STRING
324      local
325         i: INTEGER
326      do
327         from
328            i := namespaces.upper
329         until
330            Result /= Void or else i < namespaces.lower
331         loop
332            Result := namespaces.item(i).reference_at(a_namespace_ref)
333            i := i - 1
334         end
335      end
336
337   find_namespace_ref (a_namespace: UNICODE_STRING): UNICODE_STRING
338      local
339         i: INTEGER; ns: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
340      do
341         from
342            i := namespaces.upper
343         until
344            Result /= Void or else i < namespaces.lower
345         loop
346            ns := namespaces.item(i)
347            if ns.has_value(a_namespace) then
348               Result := ns.key_at(a_namespace)
349            end
350            i := i - 1
351         end
352      end
353
354   attributes_for_new_node: BOOLEAN
355
356feature {} -- Memory management
357   unused_namespaces: FAST_ARRAY[WEAK_REFERENCE[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]]
358      once
359         create Result.make(0)
360      end
361
362   new_namespaces: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
363      local
364         i: INTEGER
365      do
366         from
367            i := unused_namespaces.upper
368         until
369            Result /= Void or else i < unused_namespaces.lower
370         loop
371            Result := unused_namespaces.item(i).item
372            if Result /= Void then
373               unused_namespaces.item(i).set_item(Void)
374               Result.clear_count
375            end
376            i := i - 1
377         end
378         if Result = Void then
379            create {HASHED_BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]} Result.make
380         end
381      ensure
382         Result.is_empty
383      end
384
385   old_namespaces (a_namespaces: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING])
386      local
387         i: INTEGER; done: BOOLEAN; wr: WEAK_REFERENCE[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]; k, v: UNICODE_STRING
388      do
389         from
390            i := a_namespaces.lower
391         until
392            a_namespaces.is_empty
393         loop
394            k := a_namespaces.key(i)
395            v := a_namespaces.item(i)
396            a_namespaces.remove(k)
397            string_recycle(k)
398            string_recycle(v)
399         end
400         from
401            i := unused_namespaces.lower
402         until
403            done or else i > unused_namespaces.upper
404         loop
405            wr := unused_namespaces.item(i)
406            if wr.item = Void then
407               wr.set_item(a_namespaces)
408               done := True
409            end
410            i := i + 1
411         end
412         if not done then
413            create wr.set_item(a_namespaces)
414            unused_namespaces.add_last(wr)
415         end
416      end
417
418   string_pool: RECYCLING_POOL[UNICODE_STRING]
419      once
420         create Result.make
421      end
422
423   string_twin (unicode: UNICODE_STRING): UNICODE_STRING
424      require
425         unicode /= Void
426      do
427         if string_pool.is_empty then
428            Result := unicode.twin
429         else
430            Result := string_pool.item
431            Result.copy(unicode)
432         end
433      ensure
434         Result.is_equal(unicode)
435      end
436
437   string_recycle (unicode: UNICODE_STRING)
438      do
439         string_pool.recycle(unicode)
440      end
441
442end -- class XMLNS_PARSER
443--
444-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
445--
446-- Permission is hereby granted, free of charge, to any person obtaining a copy
447-- of this software and associated documentation files (the "Software"), to deal
448-- in the Software without restriction, including without limitation the rights
449-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
450-- copies of the Software, and to permit persons to whom the Software is
451-- furnished to do so, subject to the following conditions:
452--
453-- The above copyright notice and this permission notice shall be included in
454-- all copies or substantial portions of the Software.
455--
456-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
457-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
458-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
459-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
460-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
461-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
462-- THE SOFTWARE.